Skip to content

Webhooks

Webhooks send LoopChat events to your own server the moment they happen: a customer messages you, a message is delivered, a template is approved, and so on.

  1. Go to Settings → Developer Hub, open the Webhooks tab and click Add webhook.
  2. Enter your Endpoint URL. It must be a public http or https address.
  3. Choose the Events to send.
  4. Add Notes if you like, then save.
  5. Copy the signing secret. It’s only shown once.

You can add up to 10 webhooks per workspace.

The Webhooks tab in the Developer Hub

Event Sent when What’s in data
message.received A customer sends you a WhatsApp message message_id, from, contact_name, type, text, conversation_id, new_conversation
message.status A message you sent is sent, delivered, read or fails message_id, status, recipient, timestamp, errors
template.status Meta approves, rejects or pauses one of your templates template_id, template_name, language, status, reason
template.category Meta moves one of your templates to another category template_id, template_name, previous_category, new_category, campaigns_paused, campaigns_resumed
order.received An order comes in through the Orders API order_id, result, events
ping You click Send a test event message

Each event is a POST with a JSON body:

{
"id": "6f1d2c3a-8b4e-4f5a-9c7d-2e1b0a9f8c7d",
"topic": "message.received",
"created_at": "2026-10-19T10:15:00.000Z",
"business_account_id": "...",
"data": {
"message_id": "wamid...",
"from": "+919000000000",
"contact_name": "Asha",
"type": "text",
"text": "Is cash on delivery available?",
"conversation_id": "...",
"new_conversation": true
}
}

With these headers:

Header Contains
X-LoopChat-Event The event name
X-LoopChat-Delivery An ID for this delivery. It stays the same on retries, so use it to ignore repeats.
X-LoopChat-Signature t=<unix time>,v1=<signature>
User-Agent LoopChat-Webhooks/1

Every event is signed with your webhook’s signing secret, so you can be sure it came from LoopChat.

  1. Read t and v1 from the X-LoopChat-Signature header.
  2. Join t, a full stop and the raw request body: <t>.<raw body>.
  3. Work out the HMAC-SHA256 of that text with your signing secret, as hex.
  4. Compare it with v1 using a constant-time comparison.
  5. Reject the event if t is more than 5 minutes from your server’s clock.
import crypto from 'node:crypto';
function isFromLoopChat(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
const expected = crypto.createHmac('sha256', secret).update(`${parts.t}.${rawBody}`).digest('hex');
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
const a = Buffer.from(expected);
const b = Buffer.from(parts.v1 || '');
return fresh && a.length === b.length && crypto.timingSafeEqual(a, b);
}

Use the raw body exactly as received. If your framework turns it into an object and back, the signature won’t match.

  • Reply with any 2xx status within 5 seconds and the event counts as delivered.
  • Otherwise LoopChat tries again after 1 minute, 5 minutes, 30 minutes, 2 hours and 6 hours, then marks the delivery as failed.
  • LoopChat doesn’t follow redirects, so give the final address.

Each webhook in the Developer Hub has:

  • Send a test event: sends a ping straight away and shows the answer
  • Delivery log: recent deliveries with the time, event, result, your server’s answer, number of tries and any problem
  • Edit: change the address, events or notes, or switch the webhook off
  • Replace signing secret: makes a new secret, shown once. The old one stops working straight away.
  • Remove

Changes to the events a webhook gets take effect within about a minute.