Skip to content
API PlatformDevelopers
PATCH/v1/contacts/{contact_id}beta

Update a contact

Needs
Create and edit contacts, tags, contact groups and custom fields (crm:write)
Plan
Any plan with API access
Limits
60 a minute per key
Dry run
Yes — every check runs with ?dry_run=true, nothing changes
Undo
Send the old values back.

Changes only the fields you pass. The phone number cannot change: create a new contact instead.

Try it

Path

The contact id.

Body

Full name.

Email; `""` clears it.

Custom field values by key. `null` or `""` clears a value. Unknown keys are refused with the list of valid ones.

Group ids.

Group ids.

Protects against doing it twice if you retry: a retry with the same key gets the first answer back instead of running again.

Test mode (dry run): nothing will change
Turns test mode off for this page only. It switches back when you leave the page.

Code and response

curl -X PATCH 'https://mcp.wa-api.cloud/v1/contacts/48213?dry_run=true' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "name": "Jane A. Doe",
  "attributes": {
    "preferred_language": "en"
  }
}'

The code reads your key from $API_KEY.

Parameters

Parameters
FieldTypeWhat it is
contact_idrequiredstring | integer · pathThe contact id.pattern ^[1-9]\d{0,18}$Signed in? Pick one from your data with “My data”.

Body

Body fields
FieldTypeWhat it is
namestringFull name.1–255 characters
emailstringEmail; "" clears it.0–255 characters
attributesobjectCustom field values by key. null or "" clears a value. Unknown keys are refused with the list of valid ones.
add_to_group_idsarray of string | integerGroup ids.0–50 items
remove_from_group_idsarray of string | integerGroup ids.0–50 items

Headers

Headers
FieldTypeWhat it is
AuthorizationrequiredheaderBearer $API_KEY — your API key.
Api-VersionheaderThe API version to use, e.g. 2026-10-01. Default: the version your key is pinned to.one of: 2026-10-01 · pattern ^\d{4}-\d{2}-\d{2}$
Idempotency-KeyheaderAny unique string (8–128 characters). A retry with the same key returns the first answer instead of running twice. Kept 24 hours.pattern ^[A-Za-z0-9._:-]+$ · 8–128 characters

Response 200

The updated contact.

Response fields
FieldTypeWhat it is
idrequiredstring
actionrequiredstringone of: created, updated, unchanged
contactrequiredobject
idrequiredstring
namerequiredstring or null
phonerequiredstring or nullAs the platform shows it to this staff member: E.164 digits, or masked (e.g. "*******12345") when the workspace hides numbers from agents. Never unmasked here.
phone_maskedrequiredboolean
whatsapp_user_idrequiredstring or null
dndrequiredbooleanDo-not-disturb: the contact receives no broadcasts/marketing.
starredrequiredboolean or nullnull only when it could not be read this time (unavailable includes profile_extras).
created_atrequiredstring or null
updated_atrequiredstring or null
emailstring or null
web_visitor_idstring or null
tagsarray of object
idrequiredstring
namerequiredstring or null
groupsarray of object
idrequiredstring
namerequiredstring or null
attributesarray of objectEvery custom field value this staff member may see (manager-only fields are hidden from agents; deleted fields never appear), at most 100. Text values longer than 2000 characters are clipped.
keyrequiredstringThe custom field key (as in GET /v1/custom-fields).
labelrequiredstring or nullThe field label shown in the panel.
typerequiredstring or nulltext, textarea, number, int, float, decimal, email, phone, url, select, radio, multiselect, checkbox, boolean, date or datetime.
valuerequiredanyThe value as the panel shows it: text; checkbox/boolean "Yes"/"No"; date "YYYY-MM-DD"; datetime "YYYY-MM-DD HH:MM:SS" (UTC); multiselect "a, b"; int/float numbers; decimal a string with 8 decimals.
displayrequiredstring or nullvalue as text.
typed_valuerequiredstring | number | boolean | array of string or nullvalue typed by the field type: number/int/float/decimal → number; checkbox/boolean → true/false; date → "YYYY-MM-DD"; datetime → ISO-8601 UTC; multiselect → array of the chosen values; select/radio/text/… → string. null when empty or unreadable.
conversationsarray of objectNewest first (at most 10), a timeline summary; read messages with the inbox tools.
idrequiredstring
channel_idrequiredstring or null
staterequiredstring or null
assignedrequiredboolean
assigned_staff_idrequiredstring or null
created_atrequiredstring or null
last_activity_atrequiredstring or null
last_activity_atstring or nullLatest conversation activity (from the conversation list).
unavailablearray of stringPresent only when part of the contact could not be read this time (retry later for it): profile_extras = starred, createdAt, updatedAt and webVisitorId are null because they are unknown, not empty; conversations = the timeline (and lastActivityAt) is empty because it could not be read, not because there are none.
dry_runbooleantrue when this was a dry run: every check ran and nothing changed.

Errors

Errors are application/problem+json. Branch on code.

StatusCodeWhen
400invalid_input

A field is missing or has the wrong format. errors[] points at each field.

401unauthenticated

The Authorization header is missing, the key is unknown, expired or revoked.

403entitlement_required

The workspace's plan does not include API access (api_access).

403insufficient_scope

The key does not have the permission this operation needs.

404not_found

No contact with this id.

409conflict

Also returned while a request with the same Idempotency-Key is still running.

429rate_limited

The key or workspace went over its rate limit. Wait for Retry-After seconds.

503upstream_unavailable

A service behind the API is briefly unavailable. Safe to retry with backoff.

504timeout

The change did not finish in time. Retry with the same Idempotency-Key: it never runs twice.

Examples

Fix a name and set a custom field

Request body

{
  "name": "Jane A. Doe",
  "attributes": {
    "preferred_language": "en"
  }
}

Response 200

{
  "id": "48213",
  "action": "updated",
  "contact": {
    "id": "48213",
    "name": "Jane A. Doe",
    "phone": "+15555550123",
    "phone_masked": false,
    "email": "jane@example.com",
    "dnd": false,
    "starred": false,
    "tags": [
      {
        "id": "17",
        "name": "vip"
      }
    ],
    "groups": [
      {
        "id": "5",
        "name": "Newsletter"
      }
    ],
    "created_at": "2026-09-20T08:14:03Z",
    "updated_at": "2026-09-23T16:40:11Z",
    "whatsapp_user_id": null
  }
}

Webhook events

These events can fire after this call. Subscribe an endpoint to hear about them.

Operation path

The same operation is also at POST /v1/ops/crm_update_contact, with every field in the JSON body.