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

EventFires when
message.receivedAn inbound WhatsApp message lands
message.sentAn outbound message gets dispatched to Meta successfully
message.deliveredMeta confirms delivery to the customer
message.readCustomer opens the message
conversation.createdA new conversation opens (first inbound from this contact)
conversation.assignedAuto-assignment or manual assignment fires
conversation.closedConversation marked closed
contact.createdNew contact added (any source: webhook, manual, import)
contact.updatedContact 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 200 immediately — synchronous downstream work risks the 10-second timeout.
  • Make handlers idempotent. Use X-Badala-Delivery as 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.