Update a contact
PATCH /v1/contacts/{id}
Partially updates a contact. A property you leave out is left alone; a
property set to null clears that column. That difference is the whole
point of PATCH here - {"company": null} erases the company, {} changes
nothing (and returns 400, since a no-op update is almost always a bug).
customFields merges into the existing object, and a null value drops
that one key. tags, when present, replaces the contact’s whole tag set.
Changing externalId to one already used in the workspace returns 409.
Emits contact.updated with a changedFields list, plus
pipeline.stage_changed when pipelineStageId moved. Send
Idempotency-Key to make a retry safe. Requires scope contacts:write.
Authorizations
Section titled “Authorizations ”Parameters
Section titled “ Parameters ”Path Parameters
Section titled “Path Parameters ”Header Parameters
Section titled “Header Parameters ”Act on this workspace’s shared CRM pool. Membership is verified on every request, so an id you do not belong to falls back to personal scope rather than widening access. Omit it to use your default workspace. Applies to every CRM endpoint, not only the ones that list it here.
A unique string of your choosing (a UUID works). The first request with this key runs normally and its response is stored for 24 hours; a retry with the same key returns that stored response verbatim, with Idempotency-Replayed: true, instead of writing again. Reusing a key with a different body returns 409.
Request Body required
Section titled “Request Body required ”Every property is optional. A property you omit is left alone; a property set to null clears that column. At least one recognised property must be present. Accepts the same fields as CreateContactRequest, plus tags, which replaces the contact’s whole tag set rather than adding to it.
object
Replaces the contact’s tags. Send [] to remove them all.
Merged into the existing values. A null value drops that key; null for the whole object clears every key.
object
Example
{ "company": "Analytical Engines Ltd", "city": "London", "notes": null, "stage": "conversation", "customFields": { "lifetime_value": 5100, "churn_risk": null }}Responses
Section titled “ Responses ”Updated contact.
A CRM contact record.
object
Social platform the contact belongs to.
Handle without the leading @.
The contact’s id in your own system. The stable key the batch endpoint upserts on.
CRM pipeline stage.
Lead score 0-100 (higher = hotter). Null when not scored.
True when the score was last set by AI, false for a manual override.
Team member the contact is assigned to.
Workspace member the row is filed under. Differs from the caller in a shared pool.
Custom pipeline board the contact sits on.
Column within pipelineId.
How the contact entered the CRM (OutboundMessage, InboundMessage, Manual, CsvImport, Extension, …).
True once the contact has been soft-deleted or archived.
Custom field values keyed by field key. Null when the contact has none.
object
Attached tags (populated on GET by id, omitted in list responses).
object
Hex color
The request body or parameters failed validation.
Standard error envelope for all v1 error responses.
object
Machine-readable error code.
Human-readable explanation of the error.
Optional structured context (field-level validation errors, etc.).
Example
{ "error": "bad_request", "message": "at least one of name, username, phone is required"}Missing or invalid bearer token.
Standard error envelope for all v1 error responses.
object
Machine-readable error code.
Human-readable explanation of the error.
Optional structured context (field-level validation errors, etc.).
Example
{ "error": "unauthorized", "message": "Invalid bearer principal"}The API key does not have the required scope for this operation.
Standard error envelope for all v1 error responses.
object
Machine-readable error code.
Human-readable explanation of the error.
Optional structured context (field-level validation errors, etc.).
Example
{ "error": "forbidden", "message": "scope contacts:write is required"}The requested resource does not exist or is not owned by the caller.
Standard error envelope for all v1 error responses.
object
Machine-readable error code.
Human-readable explanation of the error.
Optional structured context (field-level validation errors, etc.).
Example
{ "error": "not_found", "message": "contact not found"}The request collides with existing state: a duplicate externalId, an Idempotency-Key replayed with a different body or still in flight, or a permanent delete blocked by records that still reference the contact.
Standard error envelope for all v1 error responses.
object
Machine-readable error code.
Human-readable explanation of the error.
Optional structured context (field-level validation errors, etc.).
Example
{ "error": "conflict", "message": "a contact with externalId 'shopify-cust-88213' already exists", "details": { "existingContactId": 4821 }}Per-key rate limit exceeded. Retry after the time indicated by X-RateLimit-Reset.
Standard error envelope for all v1 error responses.
object
Machine-readable error code.
Human-readable explanation of the error.
Optional structured context (field-level validation errors, etc.).
Example
{ "error": "rate_limited", "message": "rate limit exceeded"}Headers
Section titled “Headers ”Maximum requests allowed per minute for this key.
Requests remaining in the current window.
Unix timestamp (seconds) when the rate limit window resets.