Event webhooks
Set it up
Go to Settings, Integrations, API Access, Webhooks, add your URL and tick the events you want. Each subscription gets its own signing secret, shown once. You can have several subscriptions, for example one per system.
Every call is a POST with a JSON body and these headers:
X-Wamafy-Signature, to check the call came from us.X-Wamafy-Event, the event name, also in the body asevent.X-Wamafy-Event-Id, the event'sid, also in the body. It stays the same on every retry.X-Wamafy-Attempt,1on the first call, higher on a retry.
The event's details are inside data.
The test button on that page sends webhook.test, with a short message in data. Answer it like any other call.
message.inbound
A customer sent a message to one of your numbers.
{
"id": "3f8a1c5e-7b2d-4e9f-a1c3-5e7b9d1f3a26",
"event": "message.inbound",
"occurredAt": "2026-10-07T10:00:00.000Z",
"data": {
"messageId": "wamid.HBgMOTE5ODc2NTQzMjEwFQIAEhgUM0E",
"from": "919876543210",
"fromName": "Priya",
"whatsappNumberId": "6f1c2a9e-1b3d-4c7a-9e2f-7d4b8a1c0e55",
"type": "text",
"text": "Is the blue one in stock?",
"interactiveReplyId": null,
"mediaId": null,
"mediaCaption": null,
"conversationId": "2b7e4f10-8c3a-4d2e-b1f6-0a9c5e7d3b21",
"leadId": "9d3f6b2a-4e1c-4a8b-9f0d-2c7e5a1b3d64",
"referral": null,
"sentAt": "2026-10-07T09:59:58.000Z"
}
}typeis the WhatsApp message type:text,image,interactiveand so on. Fields that do not apply arenull, never missing.- When they tap one of your buttons or list rows,
interactiveReplyIdis theidyou gave it. referraldescribes the ad, on the first message after someone taps a Click to WhatsApp ad.sentAtis when the customer sent it;occurredAtis when we called you.- This opens the 24-hour window, so you can reply freely.
conversation.handed_off
A chatbot reached a hand-off step: it decided a person should take over. A good one to turn into a ticket or an alert.
{
"id": "8b2d4f6a-1c3e-4a5b-9d7f-2a4c6e8b0d13",
"event": "conversation.handed_off",
"occurredAt": "2026-10-07T10:02:00.000Z",
"data": {
"conversationId": "2b7e4f10-8c3a-4d2e-b1f6-0a9c5e7d3b21",
"leadId": "9d3f6b2a-4e1c-4a8b-9f0d-2c7e5a1b3d64",
"contactPhone": "919876543210",
"contactName": "Priya",
"whatsappNumberId": "6f1c2a9e-1b3d-4c7a-9e2f-7d4b8a1c0e55",
"flowId": "7a1b3c5d-2e4f-4a6b-8c0d-1e3f5a7b9c2d",
"runId": "c4e6a8b0-1d3f-4a5c-9e7b-2d4f6a8c0e19",
"note": "Customer asked about a refund",
"assignedToUserId": null,
"teamId": null,
"handedOffAt": "2026-10-07T10:02:00.000Z"
}
}noteis the text written on the hand-off step, if any.assignedToUserIdandteamIdsay who the step handed it to. Both arenullwhen it went to the general queue.- Unless the step is set to keep the chatbot running, the chatbot then stays out of the chat, so your replies are not talked over.
csat.received
A customer tapped a rating on the satisfaction survey sent after a chat.
{
"id": "5c7e9a1b-3d5f-4b6a-8c0e-4b6d8f0a2c35",
"event": "csat.received",
"occurredAt": "2026-10-07T10:30:00.000Z",
"data": {
"rating": 2,
"conversationId": "2b7e4f10-8c3a-4d2e-b1f6-0a9c5e7d3b21",
"leadId": "9d3f6b2a-4e1c-4a8b-9f0d-2c7e5a1b3d64",
"contactPhone": "919876543210",
"contactName": "Priya",
"whatsappNumberId": "6f1c2a9e-1b3d-4c7a-9e2f-7d4b8a1c0e55",
"assignedToUserId": null,
"ratedAt": "2026-10-07T10:29:58.000Z"
}
}rating is 1 to 5. assignedToUserId is whoever was handling the chat, or null.
payment.paid
A payment you asked for with Ask for a payment was paid. It is confirmed with WhatsApp's own record of the payment before we call you, so you can fulfil the order.
{
"id": "1e3a5c7e-9b2d-4f6a-8c1e-3a5c7e9b1d48",
"event": "payment.paid",
"occurredAt": "2026-10-13T10:01:12.000Z",
"data": {
"id": "5d7f9b1c-3e5a-4c7e-9a1b-3c5e7a9b1d24",
"referenceId": "wp-mv5jrp6d-aba359af",
"amount": 499,
"currency": "INR",
"to": "919876543210",
"expiresAt": "2026-10-13T10:30:00.000Z",
"refundedAmount": 0,
"refunds": [],
"createdAt": "2026-10-13T10:00:00.000Z",
"status": "paid",
"paymentId": "pay_Tn5LuVrMQaMghT",
"paidAt": "2026-10-13T10:01:10.000Z"
}
}datais the payment as Get a payment returns it: match it to your order bydata.id, the id Ask for a payment gave you.paymentIdis your payment gateway's id for the payment.
payment.expired
A payment you asked for was not paid in time: 30 minutes for a message, 3 days for a template. It can no longer be paid; ask again if you still want it.
{
"id": "9d1f3b5a-7c2e-4a6d-8f0b-1c3e5a7d9f62",
"event": "payment.expired",
"occurredAt": "2026-10-13T10:35:00.000Z",
"data": {
"id": "5d7f9b1c-3e5a-4c7e-9a1b-3c5e7a9b1d24",
"referenceId": "wp-mv5jrp6d-aba359af",
"amount": 499,
"currency": "INR",
"to": "919876543210",
"expiresAt": "2026-10-13T10:30:00.000Z",
"refundedAmount": 0,
"refunds": [],
"createdAt": "2026-10-13T10:00:00.000Z",
"status": "expired",
"paymentId": null,
"paidAt": null
}
}Delivery
- Answer with a
2xxas soon as you have the body, then do the work. Anything else, or no answer within 10 seconds, is a failure. - A failed call is retried after 1 minute, 5 minutes, 30 minutes, 2 hours and 12 hours: six attempts in all, with the same body and the same
id. So a call can arrive more than once: keep theids you have handled and ignore a repeat. - Every call is logged for 30 days. Open Recent deliveries on the webhook to see what was sent, the answer and the next retry, and Resend one that failed or that you need again.
- After 15 failed deliveries in a row (a delivery fails once all six attempts have), a subscription switches itself off. Switch it back on from the same page once your endpoint is fixed.
- Message delivery updates are not events. They have their own status webhook.