POST
Resolve or create a marketing contact

Authorizations

Authorization
string
header
required

A secret API key. Publishable keys cannot reach this API. A key may carry an expiry, and an expired key is refused exactly like an unknown one, with a 401 that names no reason; check the key's expires_at in the dashboard rather than inferring it from a response. When a merchant rolls a key's secret they choose a grace window of up to 3 days, and for its duration BOTH the new secret and the one it replaced authenticate, so an integration moves over on its own deploy schedule instead of at the instant the button is pressed. Move before the window closes: after it, the old secret is refused. Nothing else about this contract moves with a roll. The key keeps its id and its scopes, so the only thing an integration updates is the credential itself.

Headers

Idempotency-Key
string
required

A unique key per logical write. Replaying a request with the same key returns the first response byte for byte instead of applying the write twice.

Body

application/json

THIS IS THE CREATE. There is no separate one. The keys are searched strongest first (customer_id, then email, then phone_e164, then anonymous_id) and the first match wins; when nothing matches, a contact is CREATED. A pipeline pushing a malformed identity therefore does not error, it manufactures a person, so send keys you have verified. AT LEAST ONE KEY IS REQUIRED. first_name and last_name name nobody, so a body carrying only those is 400 no_identity. IDENTITY KEYS ARE WRITE-ONCE. A resolve that MATCHES an existing contact fills in a missing first or last name and changes nothing else: it will not attach an email to a contact keyed by a customer id and it will not re-point a key that is already set. That is the control that stops one caller merging two people's profiles or moving a subscribed address onto a row it does not own; correcting a key is a dashboard job, where a human can see both records. 200 ON EVERY CALL, never 201. Whether this matched or created is not something the response can honestly report: two concurrent resolves of the same address race, one inserts and the other re-selects, and both did the same thing from your side. Idempotent by identity is the property to depend on.

customer_id
string

The storefront customer this person signs in as: the strongest key, searched first. Unique per store.

anonymous_id
string

A browser identifier for someone not yet known by name: the weakest key, searched last. Unique per store.

email
string

Stored lower-cased and unique per store, so casing never splits one person into two contacts. Must be a single valid address.

phone_e164
string

E.164 with a LEADING + and a country code, for example +14155550123. A national number is refused rather than guessed at a country: guessing wrong routes a message to a different person.

first_name
string

Filled in on a match only where it is currently unset; an existing name is never overwritten.

last_name
string

Same rule as first_name.

Response

Success

data
object