WhatIsUp.dev
Get started

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:

FieldNotes
eventThe event name. See list below.
event_idULID, stable across retries. Use as your dedupe key.
api_versionDate-stamped version of the event schema. We won't break shape within a version.
instance_idThe instance that produced the event. Always present.
occurred_atWall-clock time at the gateway when the event was emitted.
dataEvent-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. Includes phone_number.
  • instance.disconnected β€” The session ended. Includes reason (network, logout, session_invalidated, kicked_other_device).
  • message.received β€” Inbound message from a contact. Includes from, body (text or media reference), and received_at.
  • message.sent β€” Your outbound message reached WhatsApp. Includes message_id.
  • message.delivered β€” The recipient's device acked the message.
  • message.failed β€” Terminal send failure. Includes error.code and error.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.