WSAPI emits real-time events whenever something happens on your WhatsApp instance — new messages, status changes, group updates, calls, and more.
Delivery modes
There are two ways to receive events:
Webhooks (Push)
SSE (Pull)
WSAPI sends an HTTP POST request to a public endpoint you configure. Best for server-based applications that already expose HTTP endpoints.
- Configure your instance with Webhook delivery mode and specify a Webhook URL
- When an event occurs, WSAPI sends a
POST request with the event payload
- Your server processes the event and responds with a
200 status code
Your application opens a persistent Server-Sent Events connection to WSAPI and receives events as they occur. Best when you don’t have a public endpoint or prefer a pull-based approach.
- Your app opens an SSE connection to WSAPI
- Events are streamed in real time as they occur
- If the connection drops, your app reconnects and resumes
We recommend using SSE mode with one of our official SDKs, which include a proper SSE client implementation with automatic reconnection.
See Configuring Event Delivery for setup instructions.
Event payload
All events follow this structure regardless of delivery mode:
{
"eventId": "evt_a1b2c3d4e5f67890",
"instanceId": "ins_ABCdefGHIjk",
"receivedAt": "2025-05-19T00:51:44.816Z",
"eventType": "message",
"eventData": {
// Event-specific data
}
}
| Field | Type | Description |
|---|
eventId | string | Unique event identifier |
instanceId | string | The instance that generated the event |
receivedAt | string | ISO 8601 timestamp when the event was received |
eventType | string | The event type (e.g., message, logged_in) |
eventData | object | Event-specific payload |
Event types
| Event | Category | Description |
|---|
logged_in | Session | Instance connected to WhatsApp |
logged_out | Session | Instance disconnected (includes reason) |
login_error | Session | Pairing error occurred |
initial_sync_finished | Session | Initial history sync completed |
message | Messages | New message received (text, media, reaction, contact, etc.) |
message_read | Messages | Message read/delivery receipt |
message_delete | Messages | Message was deleted |
message_star | Messages | Message was starred/unstarred |
message_history_sync | Messages | On-demand history sync messages |
chat_setting | Chats | Chat settings changed (mute, pin, read, archive, ephemeral) |
chat_presence | Chats | Contact is typing, recording, or paused |
chat_push_name | Chats | Contact’s push name changed |
chat_status | Chats | Chat about/status text updated |
chat_picture | Chats | Chat picture changed |
contact | Contacts | Contact information updated |
group | Groups | Group/community settings or membership changed |
newsletter | Newsletters | Newsletter/channel subscription or mute change |
user_presence | Users | User online/offline status changed |
call_offer | Calls | Incoming call received |
call_accept | Calls | Call was accepted |
call_terminate | Calls | Call ended |
Four events are system events: logged_in, logged_out, login_error, and initial_sync_finished. System events are always delivered regardless of event filter configuration and cannot be opted out of.
Every webhook POST request includes the following HTTP headers:
| Header | Description |
|---|
Content-Type | Always application/json |
X-Instance-Id | The ID of the instance that generated the event |
X-Webhook-Signature | HMAC-SHA256 signature of the request body. Only present when a signingSecret is configured. |
Verifying event signatures
When a Signing Secret is configured for your instance, WSAPI signs every event payload using HMAC-SHA256. The signature is included in the X-Webhook-Signature header with the format:
sha256=<hex-encoded HMAC-SHA256>
Always configure a signing secret to verify the authenticity of incoming events and prevent unauthorized parties from sending fake events to your endpoint.
To verify an event:
- Compute the HMAC-SHA256 of the raw request body using your signing secret as the key
- Hex-encode the result and prepend
sha256=
- Compare the computed value with the
X-Webhook-Signature header using a constant-time comparison to prevent timing attacks
const crypto = require("crypto");
function verifySignature(body, secret, headerValue) {
const expected =
"sha256=" + crypto.createHmac("sha256", secret).update(body).digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(headerValue)
);
}
Delivery and retries
- WSAPI expects a
2xx response for webhook deliveries
- If no valid response is received, WSAPI retries delivery up to 2 times
- After that, the event is discarded
- Implement idempotent processing to handle potential duplicate deliveries
Best practices
- Respond quickly — return
200 immediately and process events asynchronously
- Verify signatures — always validate the
X-Webhook-Signature on incoming events
- Handle duplicates — design your handler to be idempotent in case of retries
- Filter events — use event filtering to only receive events you need
- Use HTTPS — always use an HTTPS endpoint for your webhook URL
- Log events — log incoming events for debugging and audit purposes