Outbound webhooks
Subscribe an HTTPS endpoint to events in your org. Badala will POST a signed JSON payload to your URL whenever a subscribed event fires. Useful for syncing to your CRM, ERP, or analytics — anywhere you want a server-side reaction to inbox activity.
Create a webhook
Open Settings → Webhooks in the inbox. Click New webhook, paste your URL, pick the events you care about, save. Badala generates an HMAC secret you'll see only once at create time — copy it before closing the modal.
Available events
| Event | Fires when |
|---|---|
message.received | An inbound WhatsApp message lands |
message.sent | An outbound message gets dispatched to Meta successfully |
message.delivered | Meta confirms delivery to the customer |
message.read | Customer opens the message |
conversation.created | A new conversation opens (first inbound from this contact) |
conversation.assigned | Auto-assignment or manual assignment fires |
conversation.closed | Conversation marked closed |
contact.created | New contact added (any source: webhook, manual, import) |
contact.updated | Contact data changes (name, tags, etc.) |
Payload
Every payload has the same envelope. The data field shape varies by event type. Wire format: JSON.
POST https://your-server.com/webhook
Content-Type: application/json
X-Badala-Event: message.received
X-Badala-Delivery: dlv_abc123
X-Badala-Signature: sha256=<hex hmac>
X-Badala-Timestamp: 1715189000
{
"event": "message.received",
"deliveryId": "dlv_abc123",
"orgId": "cmowvrdnx00018z3xk8pkhvbm",
"timestamp": "2026-05-08T18:43:20.000Z",
"data": {
"messageId": "msg_xyz",
"conversationId": "conv_xyz",
"contactId": "contact_xyz",
"from": "+96567666156",
"type": "TEXT",
"body": "Hi, I'd like to book…"
}
}Verifying the signature
Compute HMAC_SHA256(secret, timestamp + "." + rawBody) and compare against the value in X-Badala-Signature (after stripping the sha256= prefix). Reject any request older than 5 minutes (replay protection).
// Node.js example
import crypto from 'node:crypto';
function verify(req, secret) {
const ts = req.headers['x-badala-timestamp'];
const sig = (req.headers['x-badala-signature'] ?? '').replace('sha256=', '');
const age = Math.abs(Date.now() / 1000 - Number(ts));
if (age > 300) return false;
const expected = crypto
.createHmac('sha256', secret)
.update(`${ts}.${req.rawBody}`)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(sig, 'hex'),
Buffer.from(expected, 'hex'),
);
}Delivery + retries
Badala expects a 2xx response within 10 seconds. Anything else (or a timeout) is retried with exponential backoff: 30s, 2min, 10min, 1h, 6h. After 5 failed attempts the delivery is marked dead and surfaced in the deliveries list.
Inspect recent deliveries — including raw request/response — under Settings → Webhooks → <your webhook> → Deliveries. You can re-fire any single delivery from there.
Test fire
From the create/edit modal, click Send test event. Badala will POST awebhook.test envelope to your URL with sample data. Use this to confirm your endpoint accepts the payload and validates the signature before subscribing real events.
Best practices
- Process the payload in a background job and return
200immediately — synchronous downstream work risks the 10-second timeout. - Make handlers idempotent. Use
X-Badala-Deliveryas a dedup key; retries reuse the same delivery ID. - One webhook URL can subscribe to many events. You don't need a separate webhook per event type.
- Rotate the HMAC secret from the edit modal if it leaks. Old secret stays valid for 5 minutes after rotation to avoid in-flight failures.