WhatsApp webhooks send events for your Edesy numbers to your own HTTPS endpoint. Register a URL, choose message.received, message.status or both, and set a signing secret. Each POST carries an X-Edesy-Signature HMAC-SHA256 header, failed deliveries are retried with backoff, and every attempt is shown in a delivery log.
Facts#
| Item | Detail |
|---|---|
| Where | wa-connect.edesy.in > Webhooks, or the API below |
| Events | message.received (a customer sent a message), message.status (sent, delivered, read, failed) |
| Filter | Pick event types per webhook; an empty list means all events |
| Multiple endpoints | Yes, register as many as you need |
| Signature | X-Edesy-Signature: sha256=<hex>, HMAC-SHA256 of the raw body with your secret |
| Success | Any 2xx response within 5 seconds |
| Retries | Up to 6 attempts, backoff of about 30 s, 1 min, 2 min, 4 min, 8 min |
| Delivery log | Event, status, attempts, HTTP code, error, next retry, payload |
| Test | Send a webhook.test event from the portal or API |
How it works#
- Add a webhook on the Webhooks page: endpoint URL, optional signing secret, and events.
- Click Test to send a
webhook.testevent and check that your endpoint returns2xx. - Verify the signature on every request (below).
- Return
2xxquickly. Do slow work after you respond. - Check the delivery log if events are missing. Failed attempts show the HTTP code and error, and pending ones show the next retry time.
Payload#
Every event uses the same envelope:
{
"event": "message.received",
"timestamp": "2026-10-28T10:15:12Z",
"data": { }
}
message.received#
data is the stored inbound message:
{
"event": "message.received",
"timestamp": "2026-10-28T10:15:12Z",
"data": {
"id": "5b1c2d3e-0000-4000-8000-000000000000",
"conversation_id": "7d8e9f00-0000-4000-8000-000000000000",
"wamid": "wamid.HBgM...",
"direction": "inbound",
"type": "text",
"status": "delivered",
"from": "+919876543210",
"to": "+911234567890",
"content": "Is my order shipped?",
"created_at": "2026-10-28T10:15:12Z"
}
}
Media messages also carry media_id, media_mime_type and, for documents, file_name. Replies carry context_message_id.
message.status#
data is the WhatsApp status update:
{
"event": "message.status",
"timestamp": "2026-10-28T10:15:20Z",
"data": {
"id": "wamid.HBgM...",
"recipient_id": "919876543210",
"status": "delivered",
"timestamp": "1761646520"
}
}
status is sent, delivered, read or failed. Failed updates include an errors array. Match data.id to the wamid you got when sending.
Verify the signature#
import crypto from 'crypto';
function isValid(rawBody, header, secret) {
const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
if (!header || header.length !== expected.length) return false;
return crypto.timingSafeEqual(Buffer.from(header), Buffer.from(expected));
}
Compute the HMAC over the raw request body, before any JSON parsing. If you did not set a secret, no signature header is sent.
Webhooks API#
Base URL: https://voice-api.edesy.in/v1/whatsapp. Auth: Authorization: Bearer sk_....
| Method | Path | Description |
|---|---|---|
| POST | /webhooks |
Register. Body: url (required), secret, event_types |
| GET | /webhooks |
List (the secret is never returned; has_secret shows whether one is set) |
| DELETE | /webhooks/{id} |
Remove |
| POST | /webhooks/{id}/test |
Send a webhook.test event; returns ok and status_code |
| GET | /webhooks/{id}/deliveries |
Recent delivery attempts (limit, default 50) |
curl -X POST https://voice-api.edesy.in/v1/whatsapp/webhooks \
-H "Authorization: Bearer sk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks/whatsapp",
"secret": "a-long-random-string",
"event_types": ["message.received", "message.status"]
}'
FAQ#
Which events can I subscribe to?#
message.received for inbound messages and message.status for sent, delivered, read and failed updates.
How do I check a webhook is genuine?#
Compute HMAC-SHA256 of the raw body with your secret and compare it to X-Edesy-Signature (sha256=<hex>).
What happens if my server is down?#
The delivery is retried up to 6 attempts with growing gaps (about 30 s, 1 min, 2 min, 4 min, 8 min). After the last attempt it is marked failed in the delivery log.
How fast must my endpoint respond?#
Within 5 seconds, with a 2xx status. Anything else counts as a failed attempt.
Can I send events to more than one URL?#
Yes. Register several webhooks, each with its own events and secret.
Can I test my endpoint before going live?#
Yes. Use Test in the portal or POST/v1/whatsapp/webhooks/{id}/test. It sends a webhook.test event.
Do I get template approval events?#
No. Template status is updated in the portal and API; use Sync with Meta or GET/v1/whatsapp/templates to check it.