Event reference
Every delivery has the same envelope:
{ "event": "message.delivered", "timestamp": "2026-08-20T09:15:04.221Z", "data": { }}And the same headers:
| Header | Meaning |
|---|---|
X-KM-Event | The event name, so you can route before parsing |
X-KM-Delivery-Id | Shared by every attempt at one event |
X-KM-Timestamp | Unix seconds, part of the signed string |
X-KM-Signature | v1=<hex>, see Verify webhook signatures |
X-Webhook-Signature | The older scheme, still sent |
Reply 2xx as fast as you can and do the work afterwards. We retry three times
with backoff on a 5xx or a timeout, and do not retry a 4xx, which cannot
succeed by repetition.
Messages
Section titled “Messages”message.incoming
Section titled “message.incoming”A contact sent you a message.
{ "message_id": "6f1c2d3e-...", "contact_id": "3f2a...", "contact_phone": "+966500000000", "contact_name": "Sara", "message_type": "text", "content": "Has it shipped yet?", "whatsapp_account": "Main"}This also opens the 24-hour window for that contact, so it is the moment free text becomes possible again.
message.sent
Section titled “message.sent”A message left K-Message, whether from an agent, the chatbot or the API. It means we handed it to WhatsApp, not that anyone received it.
message.delivered
Section titled “message.delivered”WhatsApp confirmed the message reached the recipient’s device.
{ "message_id": "6f1c2d3e-...", "contact_id": "3f2a...", "contact_phone": "+966500000000", "wamid": "wamid.HBgMOTY2NTAwMDAwMDAwFQIAERgS...", "status": "delivered"}This is the event to record against your own order or ticket. wamid is Meta’s
identifier, which is what you will be looking at if you ever compare our
records with your Meta dashboard.
message.read
Section titled “message.read”The recipient opened it. Not all recipients have read receipts enabled, so absence of this event means nothing.
message.failed
Section titled “message.failed”WhatsApp refused the message.
{ "message_id": "6f1c2d3e-...", "contact_id": "3f2a...", "contact_phone": "+966500000000", "status": "failed", "error_message": "Message failed to send because more than 24 hours have passed since the customer last replied to this number"}error_message is Meta’s own text. The most common causes are being outside
the 24-hour window, a template that is no longer approved, and a number that is
not on WhatsApp.
Contacts
Section titled “Contacts”contact.created
Section titled “contact.created”A contact was added, either by messaging you for the first time or through the API.
{ "contact_id": "3f2a...", "contact_phone": "+966500000000", "contact_name": "Sara", "whatsapp_account": "Main"}contact.updated
Section titled “contact.updated”A contact’s details or tags changed.
{ "contact_id": "3f2a...", "contact_phone": "+966500000000", "contact_name": "Sara Al-Ahmed", "tags": ["vip", "riyadh"]}Subscribe to both if you mirror contacts into your own system. A sync that heard only about creations would drift out of date on the first edit.
Handovers
Section titled “Handovers”transfer.created
Section titled “transfer.created”A conversation asked for a human agent, either because the customer requested one or because the chatbot escalated.
{ "transfer_id": "4e5f6a7b-...", "contact_id": "3f2a...", "contact_phone": "+966500000000", "contact_name": "Sara", "source": "chatbot", "reason": "Customer asked to speak to someone", "whatsapp_account": "Main"}transfer.assigned
Section titled “transfer.assigned”An agent picked it up. Carries agent_id and agent_name.
transfer.resumed
Section titled “transfer.resumed”The handover closed and the conversation went back to the chatbot.
Campaigns
Section titled “Campaigns”campaign.completed
Section titled “campaign.completed”A bulk send finished, with its settled counts.
{ "campaign_id": "5d4c3b2a-...", "campaign_name": "Eid offer", "status": "completed", "total_recipients": 1200, "sent": 1197, "delivered": 1102, "read": 640, "failed": 3, "skipped": 0}Fires once, when every recipient has been dealt with. A campaign parked against the daily allowance has not completed and does not fire this.
Templates
Section titled “Templates”template.approved
Section titled “template.approved”Meta approved a template and it can now be sent.
{ "template_id": "7c6b5a4d-...", "template_name": "order_shipped", "language": "ar", "status": "APPROVED"}template.rejected
Section titled “template.rejected”Meta rejected it, with the reason when one was given.
{ "template_id": "7c6b5a4d-...", "template_name": "order_shipped", "language": "ar", "status": "REJECTED", "reason": "INVALID_FORMAT"}Meta’s review is asynchronous and can take hours. These two events exist so a system that submits templates does not have to poll for the outcome.