Skip to content

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 an X-SendAPI-Signature header containing the HMAC-SHA256 signature of the raw request body, signed with your webhook secret.

javascript
const crypto = require('crypto');

function verifyWebhook(rawBody, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');
  return `sha256=${expected}` === signature;
}

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": "msg_01H8...QP",
    "session_id": "sess_01H8BKF...Z2T",
    "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": "msg_01H8...QP",
    "session_id": "sess_01H8BKF...Z2T",
    "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": "msg_01H8...QP",
    "session_id": "sess_01H8BKF...Z2T",
    "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": "msg_01H8...QP",
    "session_id": "sess_01H8BKF...Z2T",
    "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": "msg_01H8...QP",
    "session_id": "sess_01H8BKF...Z2T",
    "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": "sess_01H8BKF...Z2T",
    "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": "sess_01H8BKF...Z2T",
    "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": "sess_01H8BKF...Z2T",
    "qr_code": "data:image/png;base64,iVBORw0KGgoAAAA..."
  }
}

Group Events

group.participant_added

json
{
  "event": "group.participant_added",
  "data": {
    "session_id": "sess_01H8BKF...Z2T",
    "group_id": "grp_01H9...XT",
    "participant": "+250788000000",
    "added_by": "+447700900000"
  }
}

group.participant_removed

json
{
  "event": "group.participant_removed",
  "data": {
    "session_id": "sess_01H8BKF...Z2T",
    "group_id": "grp_01H9...XT",
    "participant": "+14155552671",
    "removed_by": "+447700900000"
  }
}

Retry Policy

Webhook deliveries are retried up to 4 times with exponential backoff if your endpoint returns a non-2xx status:

AttemptDelay
1 (initial)Immediate
25 seconds
330 seconds
42 minutes
510 minutes

After 5 failed attempts, the delivery is marked as failed and no further retries are made. Check the Webhook logs in your dashboard to review failed deliveries and retry them manually.

Released under the MIT License.