Sendexa LogoDocs

Webhooks

Receive real-time event notifications for outbound delivery updates and inbound messages from your users — no polling required.

Real-time Events

Receive instant push notifications for message sent, delivered, read, and failed events

Inbound Messages

Handle replies from users — text, media, buttons, and list responses

HMAC Verification

Verify every payload with HMAC-SHA256 signatures to protect against spoofing

Auto Retry

Missed events are retried up to 5 times with exponential backoff

Delivery Latency

<500ms

event to webhook

Max Retries

5

with backoff

Event Types

6

delivery + inbound

Signature Algo

SHA256

HMAC verification

Event Types

message.sent

Message dispatched to WhatsApp network

message.delivered

Message delivered to recipient device

message.read

Message opened and read by the recipient

message.failed

Delivery failed — error details included in payload

message.inbound

User replied or sent a message to your WhatsApp number

message.deleted

User deleted the message before reading

Webhook Payloads
JSON
{
"event": "message.delivered",
"timestamp": "2024-01-15T10:30:03Z",
"data": {
"messageId": "exa_wa_123456789_abc123def",
"wamid": "wamid.HBgLMjMzNTU1MzM5NTIVAgARGBI2QzE5QUI3RjZENkI2QTU3NjQA",
"to": "233244000000",
"type": "text",
"deliveredAt": "2024-01-15T10:30:03Z"
}
}

Signature Verification

Every webhook request includes an X-Sendexa-Signature header containing an HMAC-SHA256 hash of the raw request body signed with your webhook secret.

TypeScript
import crypto from 'crypto';
function verifyWebhookSignature(
rawBody: string,
signature: string,
secret: string
): boolean {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody, 'utf8')
.digest('hex');
// Use timingSafeEqual to prevent timing attacks
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}

Webhook Handler Examples

JavaScript
import express from 'express';
import crypto from 'crypto';
const app = express();
// Parse raw body for signature verification
app.use('/webhooks/whatsapp', express.raw({ type: 'application/json' }));
app.post('/webhooks/whatsapp', (req, res) => {
const signature = req.headers['x-sendexa-signature'];
const secret = process.env.WHATSAPP_WEBHOOK_SECRET;
// 1. Verify signature
const expected = crypto
.createHmac('sha256', secret)
.update(req.body)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
return res.status(401).json({ error: 'Invalid signature' });
}
const payload = JSON.parse(req.body.toString());
// 2. Handle events
switch (payload.event) {
case 'message.delivered':
console.log(`Delivered: ${payload.data.messageId}`);
break;
case 'message.read':
console.log(`Read: ${payload.data.messageId}`);
break;
case 'message.failed':
console.error(`Failed: ${payload.data.messageId}`, payload.data.error);
// Trigger retry logic here
break;
case 'message.inbound':
const { from, text } = payload.data;
console.log(`Reply from ${from}: ${text?.body}`);
// Route to your support system
break;
default:
console.log(`Unknown event: ${payload.event}`);
}
// 3. Always respond 200 quickly
res.status(200).json({ received: true });
});
app.listen(3000);

Retry Behaviour

AttemptDelayNotes
#1ImmediateFirst delivery attempt
#230 secondsOn non-2xx response or timeout
#32 minutesExponential backoff
#410 minutesExponential backoff
#530 minutesFinal attempt — event dropped if failed