Event payloads
Every webhook body has the same envelope:
{
"event": "message.received",
"event_id": "evt_01J...",
"api_version": "2026-04",
"instance_id": "inst_01J...",
"occurred_at": "2026-05-01T12:34:56.000Z",
"data": { /* event-specific shape, see below */ }
}The envelope fields:
| Field | Notes |
|---|---|
event | The event name. See list below. |
event_id | ULID, stable across retries. Use as your dedupe key. |
api_version | Date-stamped version of the event schema. We won't break shape within a version. |
instance_id | The instance that produced the event. Always present. |
occurred_at | Wall-clock time at the gateway when the event was emitted. |
data | Event-specific payload, schemas below. |
Event types
The full list of events comes straight from @whatisup/contracts:
qr.updatedβ A new QR PNG is available for an instance. The dashboard uses this; webhook subscribers usually filter it out.instance.connectedβ The instance finished pairing. Includesphone_number.instance.disconnectedβ The session ended. Includesreason(network,logout,session_invalidated,kicked_other_device).message.receivedβ Inbound message from a contact. Includesfrom,body(text or media reference), andreceived_at.message.sentβ Your outbound message reached WhatsApp. Includesmessage_id.message.deliveredβ The recipient's device acked the message.message.failedβ Terminal send failure. Includeserror.codeanderror.message.
Headers your endpoint will see
POST /your/endpoint HTTP/1.1
Host: api.acme.dev
Content-Type: application/json
User-Agent: whatisup-webhooks/0.0.1
X-WhatIsUp-Signature: t=1700000000,v1=...
X-WhatIsUp-Event: message.received
X-WhatIsUp-Event-Id: evt_01J...
X-WhatIsUp-Correlation-Id: req_01J...
X-WhatIsUp-Event-Id and X-WhatIsUp-Event are conveniences β they mirror what's in the body, but let you route or short-circuit before you parse the JSON.
X-WhatIsUp-Correlation-Id traces the event back to whatever triggered it. For an outbound message.sent it's the request ID of the POST /v1/messages call. For a message.received it's a generated ID stable across retries. Useful in logs.
Compatibility
We versioned the schemas via api_version. Within 2026-04, we'll only add optional fields β never rename, never remove. New incompatible shapes get a new version string; you opt in by upgrading your endpoint at your pace. (We don't have multiple versions live yet β just one.)
The wire payloads here are the same shapes that come down /v1/events. The SSE stream is just an unsigned, untoaster real-time view of the same bus.