Skip to main content
POST
Error

Authorizations

Authorization
string
header
required

An organization API key, uk_sk_live_… or uk_sk_test_…. The environment is resolved from the stored key row on every request, never from a request parameter.

Every route behind this credential is rate limited per key — 1000 requests a minute — with a second ceiling of 3000 a minute across all the keys of one environment, so splitting your traffic across keys isolates it and minting more keys does not buy more of it. A test key can never spend a live key's allowance. Every response carries the current state in headers; see the rate-limits guide.

Headers

Idempotency-Key
string

A key you choose — a UUID, or your own identifier for the operation — that makes this write safe to retry. Send the same key again and you get the same response back: the same status, the same body, and Idempotent-Replay: true. The handler does not run a second time.

The key is scoped to your API key and the route, so keys never collide between environments or tenants. It is remembered for 24 hours. Reusing it for a different request is refused (422), and a second request arriving while the first is still running is refused too (409) — retry that one in a moment.

A request that failed with a 5xx or was rate limited leaves the key spendable: retry it with the same key.

Maximum string length: 255

Body

application/json
external_id
string

The developer's own primary key for this person.

email
string<email>
phone
string

Where a WhatsApp message can reach this person, in the international form with the country code (5511987654321). Punctuation is fine; the number is normalised to E.164 without the plus before it is stored.

It becomes an identity of the contact, beside the email and your own external_id, so the merge and the erasure already know about it. It never resolves a contact, though: operators recycle numbers, and whoever gets a reused one is not the person who had it. A number is attached when it is free and ignored when it already belongs to somebody — a message that does not go out, rather than one that goes to the wrong stranger.

A number with no country code, or one carrying a national trunk prefix (011…), is a 400. We do not guess the country: a default would make the same digits mean different people in different environments.

anonymous_id
string

The device id captured before this person was known.

name
string
customer
object

Required. The team this person belongs to, named by YOUR id for it.

Upserted on every call, so a login is the whole sync: the team is created the first time and found every time after, the membership is created or its role corrected, and a call that changed nothing announces nothing. Two people of the same account land on the same team.

It is required rather than optional on purpose. Everything the customer plane can answer about a team — entitlements, a customer-scoped onboarding step, seat billing — needs one to resolve against, and you are the only system that knows which one this person belongs to. Optional, it would be the field everybody means to send later: the roster stays empty, every team question keeps answering "no team", and nothing says why.

attribution
object

First touch, captured on the visitor at first load and preserved through a merge. All fields optional; empty means unknown.

Response

The contact already existed and was updated.

contact
object

One of the developer's own users — anonymous or identified.

created
boolean