POST to it when
something happens in your workspace. Every delivery is signed, retried on
failure, and identified so you can deduplicate.
Events
These four are the whole list. An endpoint must subscribe to at least one, and
an unknown event name is refused when you register.
The delivery
Each delivery is aPOST with this envelope:
ididentifies the event, not the attempt. A retry carries the sameid, so deduplicate on it.occurred_atis when the thing happened, not when we last tried to tell you.account_idis the workspace. It is the same for every delivery to a given endpoint.
The same three values are also sent as
X-Converse360-Event,
X-Converse360-Webhook-Id and X-Converse360-Signature. They are the retired
names, kept so receivers written before the rename keep working. Read either
pair, not both; new receivers should read X-Watx-*.data per event
message.received carries channel on Instagram and website events and omits
it on WhatsApp, where the platform message id is whatsapp_message_id. A
contact created from an Instagram thread has phone: null and carries
instagram_username instead. Treat data as an open object: read the keys you
need and ignore the rest.
message.status_updated reports WhatsApp delivery only — Instagram has no
delivery receipt. The same status can arrive more than once and statuses can
arrive out of order, because that is how the platform reports them.
Register an endpoint
All five management calls need thewebhooks:manage scope.
url must be https:// and must resolve to a public address. Loopback,
private network and link-local targets are refused — at registration and again
on every delivery attempt, because DNS can change underneath us.
The rest of the management surface:
GET /webhooks— list every endpoint in the workspace. Never returns secrets.GET /webhooks/{id}— read one.PATCH /webhooks/{id}— changeurl,eventsoris_active.DELETE /webhooks/{id}— remove one. Deliveries stop immediately.
Zapier and n8n are the same endpoints
Connecting Zapier or n8n from the dashboard registers exactly this kind of endpoint, subscribed to the same four events and delivered the same way. Each row carries a read-onlyprovider of
api, zapier or n8n, saying which surface created it.
GET /webhooks returns all of them, so you will see endpoints you did not
create through the API — and deleting one here disconnects that integration.
provider has no effect on delivery; it only decides which dashboard page
manages the endpoint. The Send test event button lives on those pages and
sends a zapier.test or n8n.test event; there is no test endpoint in the API.
Verify the signature
v1 is HMAC-SHA256(secret, "<t>.<raw request body>"), hex-encoded. Compute it
over the raw bytes of the request — a body that has been parsed and
re-serialised will not match — compare in constant time, and reject a timestamp
older than a few minutes so an old delivery cannot be replayed at you.
t it carries is the time that attempt was made.
Delivery, retries and auto-disable
- Success is any 2xx. Anything else is a failure. Answer quickly and do your work afterwards: a delivery that takes longer than 5 seconds is abandoned and treated as a failure.
- Redirects are not followed. A
301or302counts as a failure, so register the final URL. - Retries. A timeout, a connection error, a
5xx, a408or a429is retried — up to 5 attempts, with exponential backoff starting at about 3 seconds. Every other4xxis treated as permanent and not retried: your receiver has said the request is wrong, and it will be just as wrong later. - One endpoint at a time. Each endpoint gets its own delivery, so one receiver being down does not delay or duplicate anyone else’s.
- Auto-disable. A failed event — after its retries are spent — counts once
against the endpoint. After 15 consecutive failures the endpoint is set
is_active: falseand deliveries stop. Any successful delivery resets the count to zero. - Re-enable with a PATCH.
{"is_active": true}turns the endpoint back on and clears the failure counter at the same time.
failure_count and last_delivery_at on each endpoint are how you check this
from the outside: a climbing count with an old last_delivery_at means your
receiver is refusing deliveries.