Webhooks
Receive real-time events when message status changes. All deliveries are signed with HMAC-SHA256.
Events
| Event | Description |
|---|---|
| message.sent | Message sent to WhatsApp server |
| message.delivered | Message delivered to recipient's phone |
| message.read | Recipient opened the chat |
| message.failed | Message failed to send |
| message.received | Incoming message from a user |
Signature verification
Every webhook delivery includes an X-Wayul-Signature header:
X-Wayul-Signature: t=<unix-timestamp>,v1=<hmac-sha256>
The HMAC is computed over body + "." + timestamp using your webhook secret. Verify the timestamp is within ±5 minutes tolerance.
Node.js verification example
import { createHmac, timingSafeEqual } from 'node:crypto';
function verifyWebhook(secret, body, headerValue) {
const match = headerValue.match(/^t=(\d+),v1=([0-9a-f]+)$/);
if (!match) return false;
const ts = Number(match[1]);
const v1 = match[2];
// Check timestamp tolerance (±5 minutes)
if (Math.abs(Date.now() - ts) > 5 * 60 * 1000) return false;
// Compute expected signature
const expected = createHmac('sha256', secret)
.update(`${body}.${ts}`)
.digest('hex');
return timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
}Retry policy
Failed deliveries are retried automatically up to 5 times with exponential backoff. Each delivery has a unique delivery_id for idempotency — your handler should process each delivery_id only once.
Payload format
{
"event": "message.delivered",
"delivery_id": "dlv_abc123",
"message_id": "msg_9f8ec2a1",
"device_id": "dev_01",
"to": "628123456789",
"timestamp": 1723273458421,
"data": {
"status": "delivered",
"content": "Kode OTP Anda: 482913"
}
}