Webhooks
A webhook is a web address on your server that KiliChat calls whenever something happens, like a customer sending a message. Your system can then react straight away: create a ticket, update an order, notify a salesperson.
Only admins can add webhooks. Each WhatsApp number can have up to 5 webhooks.
How do I add a webhook?
- Choose the WhatsApp number in the left sidebar, then click API & webhooks.
- In the Webhooks card, click Add webhook.
- Type the Endpoint URL: a public
https://address on your server, likehttps://example.com/kilichat-webhook. - Under Send me, tick the events you want.
- Click Add webhook.
- Copy the signing secret (it starts with
whsec_). It's shown once. Your endpoint uses it to check requests really come from KiliChat. - Click I've saved it.
Then click Send test to check your endpoint answers. You'll see "Test delivered (answered 200)" or "Test failed: …".
Which events can I get?
| Event | When it's sent |
|---|---|
message.received | A customer sends a message |
message.sent | A message goes out from your team, a bot, a broadcast, a sequence or the API |
message.status | A sent message is delivered, read or fails |
contact.created | A new contact is added |
contact.opted_out | A contact opts out, by keyword, by your team or through the API |
contact.opted_in | A contact opts back in |
ping | Only from Send test |
What does a request look like?
Each event is a POST with a JSON body:
POST /kilichat-webhook HTTP/1.1
Content-Type: application/json
User-Agent: KiliChat-Webhooks/1.0
X-KiliChat-Event: message.received
X-KiliChat-Delivery: 9a7e0d52-…
X-KiliChat-Timestamp: 1762596000
X-KiliChat-Signature: sha256=5f2b…{
"id": "9a7e0d52-…",
"event": "message.received",
"numberId": "…",
"createdAt": "2026-11-08T10:00:00.000Z",
"data": {
"message": {
"id": "…",
"conversationId": "…",
"direction": "in",
"type": "text",
"text": "Is the blue saree available?",
"status": "received",
"error": null,
"sentBy": "contact",
"createdAt": "2026-11-08T10:00:00.000Z"
},
"contact": {
"id": "…",
"phone": "919876543210",
"name": "Priya",
"optedOut": false,
"attributes": { "city": "Chennai" },
"lastMessageAt": "2026-11-08T10:00:00.000Z",
"createdAt": "2026-10-01T09:12:00.000Z"
}
}
}- Message events (
message.received,message.sent,message.status) carrymessageandcontact. - Contact events carry
contactonly. pingcarries{"message": "Test from KiliChat"}.
Headers
| Header | Value |
|---|---|
X-KiliChat-Event | The event name |
X-KiliChat-Delivery | A unique id for this delivery, the same as the body's id. Use it to ignore repeats. |
X-KiliChat-Timestamp | When it was sent, in Unix seconds |
X-KiliChat-Signature | sha256= and the HMAC-SHA256 of <timestamp>.<raw body> with your signing secret. See Verify signatures. |
Message fields
| Field | Values |
|---|---|
direction | in (from the customer) or out |
type | text, template, image, video, audio, document, sticker, location, address, buttons, list, link… Button and list taps arrive as text with the option's title. |
text | The text, caption, or for templates the filled-in message |
status | received for incoming; queued, sent, delivered, read or failed for outgoing |
error | Meta's reason when failed, otherwise null |
sentBy | contact, user (your team), bot, broadcast, system (automatic replies) or api |
How should my endpoint answer?
- Answer with any 2xx status within 10 seconds. Do the slow work afterwards, for example in a background job.
- Check the signature before trusting the request.
- Use
X-KiliChat-Deliveryto skip deliveries you've already handled. A retry has the same id. - Events can arrive out of order. For example,
message.statuswithreadmight come beforedelivered. Go by thestatusvalue rather than the order.
Redirects aren't followed, so give the final URL.
Which URLs are allowed?
Endpoints must be public https:// addresses. Private, local and link-local addresses (like localhost, 192.168.x.x or 10.x.x.x) are refused with "Use a public address, not a private network". Usernames and passwords in the URL aren't allowed.
To test on your own computer, use a tunnelling tool that gives you a public https:// address.
How do I see what was sent?
Click Deliveries on the webhook. It shows the last 50 deliveries with their Event, Result (Delivered, Retrying or Failed), number of Tries and When. Click a row to see the exact body that was sent. Click Retry on a failed delivery to send it again.
The webhook card also shows when it last sent, what your endpoint answered, and how many were delivered and failed today.
How do I change, pause or delete a webhook?
- Edit: change the URL or events.
- Turn off / Turn on: pause and resume. While a webhook is off, new events aren't queued for it.
- New secret: makes a new signing secret. "The old one stops working straight away", so update your server at the same moment.
- Bin icon: deletes the webhook.
Can I receive photos and files customers send?
The message.received event tells you a file arrived (type is image, document and so on, and text has any caption), but it doesn't include the file itself. Your team can open it in the KiliChat inbox. To collect files automatically, use a flow's Ask for a file step, which saves a link on the contact.
See also Rate limits and retries for what happens when your endpoint is down.