Skip to main content
Your product needs to know when a customer writes in, when the assistant answers, and when somebody needs a person. You register one endpoint and it receives events for every merchant you own — one URL, one secret, rather than a pair per merchant.

Registering

The secret is returned exactly once. Store it before you close the response. There is no endpoint that will show it to you again — only secret_hint, the last four characters, so you can tell two keys apart.
Rotation is not an outage. The old key stays valid for 24 hours and we sign with both, so you add the new one, confirm it works, then drop the old one.
What your endpoint may receive is bounded by the scopes on the key that created it. A key without messages:read registers fine and gets no message.* events. Create the endpoint with the key you actually intend to run on.

The envelope

tenant_ref is the field you want. It is your identifier for the merchant — the same string you provisioned them with and use in every URL. tenant is our internal id and you have never seen it.
The envelope shape is fixed per endpoint, not per deploy, so your parser will not change under you. Partner endpoints default to 2026-06-01.

Verifying

Sign "{t}." followed by the exact raw body bytes with HMAC-SHA256 and your secret, compare in constant time, and reject anything older than 300 seconds.
Verify against the bytes you received, not a re-serialised copy. Parsing and re-encoding JSON changes whitespace and key order, and the signature will never match again.
During a rotation window there may be several v1= values. Accept the delivery if any of them matches.

Events

GET /api/v2/webhooks/catalogue/ returns the live list, including the resource each event carries and the scope it needs. Your code probably wants that rather than this table.
message.received fires for media before the voice note is transcribed, so channel.media.transcription may be empty on arrival. Render the message immediately rather than waiting — a customer’s voice note appearing thirty seconds late looks like a broken inbox.

Delivery behaviour

Three attempts: immediately, then 60s, 5min and 30min. 5xx responses, timeouts and connection failures are retried. 4xx is not — a 4xx says our payload is wrong, and sending the same thing again will not make it right.Our timeout is 5 seconds. Acknowledge with a 2xx as soon as you have the body and do the work afterwards; a slow handler becomes a retried handler.
Deliveries are independent, so message.sent can reach you before the message.received it answers — and a shared endpoint interleaves every merchant’s traffic.Dedupe on id (evt_…), which is stable across retries, and order by created_at within a conversation. Never order by arrival.
One endpoint carrying every merchant is a different traffic shape from a single tenant’s. When you exceed the rate we hold deliveries back and retry with a growing delay rather than failing them — our throttle is not your failure and must not cost you an event.Per-merchant fairness still applies underneath, so one busy merchant cannot starve the rest of your book.
Merchants cannot see, list or replay deliveries to your endpoint. It is your infrastructure and they never configured it.

Requirements

Endpoints must be https and publicly resolvable. Private, loopback and link-local addresses are refused when the endpoint is created, not silently at delivery time.
Before you go live: secret stored, signature verified against raw bytes, timestamp checked, deduping on evt_ id, not relying on arrival order, and answering 2xx within five seconds.