Automations & WhatsApp

Outgoing webhooks

BMexa calls your own URL with a signed JSON body whenever a lead is received, updated, or gets a remark, so your ERP, data warehouse or automation tool stays in step without CSV exports.

Setting one up

  1. Go to Marketing › Automations › Outgoing webhooks and click Add webhook.
  2. Give it a name, paste the HTTPS URL your system listens on, tick the events (New lead received, Lead updated, Remark added) and add any header your endpoint needs, such as an API key.
  3. Click Create webhook and copy the signing secret: it is shown once. Use Rotate secret if you lose it.
  4. Click Send test. Your endpoint receives a made-up lead with the event lead.test and the card shows its answer.

What a call looks like

Every call is an HTTP POST with Content-Type application/json and these headers: X-BMexa-Event (the event name), X-BMexa-Delivery (a unique id you can use to ignore repeats), X-BMexa-Webhook, X-BMexa-Timestamp (unix seconds) and X-BMexa-Signature. The body is one JSON object:

  • lead.created fires once per new lead from any source, imports included.
  • lead.updated fires when the enquiry row changes (stage, assigned person, temperature, details). It carries the current snapshot, not the difference; several changes within a few seconds arrive as one call.
  • lead.remark_added fires for every remark, with the remark under "remark". System remarks (WhatsApp sends, imports) are included.
  • Answer with any 2xx status within ten seconds. Anything else is retried after 1, 5, 30, 120 and 720 minutes, then marked failed. Twenty consecutive failures pause the webhook until you tick Active again.
{
  "event": "lead.created",
  "deliveryId": "6d2f…",
  "sentAt": "2026-10-06T09:30:00.000Z",
  "webhook": { "id": "…", "name": "Our ERP" },
  "lead": {
    "id": "…", "code": "ENQ-1791311029293", "status": "OPEN", "temperature": "WARM",
    "enquiredAt": "2026-10-06T09:30:00.000Z", "updatedAt": "2026-10-06T09:30:00.000Z",
    "source": "KP Mart website (Wix)", "provider": "wix", "kind": "WEBSITE", "campaign": "Diwali launch",
    "city": "Noida", "purpose": "Investment", "budget": "1.2 Cr", "project": "Aranya Greens",
    "unassigned": false,
    "assignedTo": { "id": "…", "name": "Priya Sharma", "email": "priya@yourcompany.com" },
    "client": { "id": "…", "name": "Rohit Verma", "phone": "+919876543210", "email": "rohit@example.com" },
    "formFields": { "Preferred floor": "Ground" },
    "latestRemark": { "text": "Looking for a 3 BHK", "at": "2026-10-06T09:30:00.000Z", "by": "Priya Sharma" },
    "url": "https://your-bmexa/dashboard/clients/…"
  },
  "remark": { "id": "…", "text": "Called, visit on Saturday", "at": "…", "by": "Priya Sharma" }
}

Verifying the signature

The signature header has the form t=<timestamp>,v1=<hex>. Compute HMAC-SHA256 over the string "<timestamp>.<raw body>" with your secret and compare it with v1 using a constant-time comparison; reject calls whose timestamp is more than five minutes old. In Node:

const crypto = require('crypto');
function verify(secret, rawBody, header) {
  const parts = Object.fromEntries(header.split(',').map((kv) => kv.split('=')));
  const expected = crypto.createHmac('sha256', secret).update(parts.t + '.' + rawBody).digest('hex');
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
  return fresh && crypto.timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(parts.v1, 'hex'));
}

Deliveries and troubleshooting

  • The webhook page lists every delivery with your endpoint’s status code, the time it took and the next retry; Retry re-sends one, Send pending now runs the worker immediately.
  • “No answer within 10 seconds”: acknowledge first, process later; a slow endpoint is treated as failed.
  • Zapier, Make and n8n accept these calls directly as a Webhook trigger; paste their catch-hook URL as the webhook URL.

Updated on 6 October 2026