Appearance
WhatsApp Webhook Events
SendAPI fires HTTP POST requests to your webhook URL whenever something happens on your WhatsApp sessions. All payloads are signed with HMAC-SHA256 for verification.
Webhook Security
Every webhook request includes two headers:
| Header | Value |
|---|---|
X-SendAPI-Event | The event type, e.g. message.received. |
X-SendAPI-Signature | The HMAC-SHA256 hex digest of the raw request body, signed with your webhook secret. No sha256= prefix. |
javascript
const crypto = require('crypto');
function verifyWebhook(rawBody, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expected, 'hex')
);
}Event Payload Structure
All webhook events share a common envelope:
json
{
"event": "message.received",
"timestamp": "2026-03-09T14:30:00Z",
"data": { ... }
}Message Events
message.received
Fired when an inbound WhatsApp message arrives at your session.
json
{
"event": "message.received",
"timestamp": "2026-03-09T14:30:00Z",
"data": {
"message_id": 14,
"session_id": 8,
"from": "+447700900000",
"type": "text",
"content": { "body": "Hi, I need help with my order." },
"received_at": "2026-03-09T14:30:00Z"
}
}message.sent
Fired when an outbound message is confirmed sent to WhatsApp servers.
json
{
"event": "message.sent",
"timestamp": "2026-03-09T14:30:01Z",
"data": {
"message_id": 14,
"session_id": 8,
"to": "+14155552671",
"type": "text",
"status": "sent"
}
}message.delivered
Fired when the message is delivered to the recipient's device (double grey checkmark).
json
{
"event": "message.delivered",
"timestamp": "2026-03-09T14:30:22Z",
"data": {
"message_id": 14,
"session_id": 8,
"delivered_at": "2026-03-09T14:30:22Z"
}
}message.read
Fired when the recipient opens and reads the message (blue checkmarks).
json
{
"event": "message.read",
"timestamp": "2026-03-09T14:31:05Z",
"data": {
"message_id": 14,
"session_id": 8,
"read_at": "2026-03-09T14:31:05Z"
}
}message.failed
Fired when a message delivery fails permanently.
json
{
"event": "message.failed",
"timestamp": "2026-03-09T14:30:05Z",
"data": {
"message_id": 14,
"session_id": 8,
"error_code": "131026",
"error_message": "Message undeliverable: recipient is not a WhatsApp user."
}
}Session Events
session.connected
Fired when a device scans the QR code and the session becomes active.
json
{
"event": "session.connected",
"timestamp": "2026-03-09T14:30:00Z",
"data": {
"session_id": 8,
"phone_number": "+447700900000"
}
}session.disconnected
Fired when a session loses connection. reconnecting: true means the SDK will attempt to recover automatically.
json
{
"event": "session.disconnected",
"timestamp": "2026-03-09T15:00:00Z",
"data": {
"session_id": 8,
"reason": "connection_lost",
"reconnecting": true
}
}session.qr_updated
Fired when a new QR code is generated for a session (e.g., previous one expired).
json
{
"event": "session.qr_updated",
"timestamp": "2026-03-09T14:32:00Z",
"data": {
"session_id": 8,
"qr_code": "data:image/png;base64,iVBORw0KGgoAAAA..."
}
}Group Events
group.participant_added
json
{
"event": "group.participant_added",
"data": {
"session_id": 8,
"group_id": 3,
"participant": "+250788000000",
"added_by": "+447700900000"
}
}group.participant_removed
json
{
"event": "group.participant_removed",
"data": {
"session_id": 8,
"group_id": 3,
"participant": "+14155552671",
"removed_by": "+447700900000"
}
}Retry Policy
Webhook deliveries are retried with exponential backoff if your endpoint returns a non-2xx status or doesn't respond within 10 seconds. 4 attempts total:
| Attempt | Delay since previous attempt |
|---|---|
| 1 (initial) | Immediate |
| 2 | 5 seconds |
| 3 | 30 seconds |
| 4 | 2 minutes |
If all 4 fail, the delivery is recorded as failed in your dashboard. View GET /v1/webhooks/{id}/deliveries to inspect failed attempts.