Skip to content
Webhooks

Payload Examples

These are real payload shapes produced by the production publisher. Use them as fixtures in your handler tests — a regression in your parser will fail CI instead of silently swallowing live events.

Every webhook POST body conforms to one of two shapes, distinguished by the X-Kirim-Source header:

// X-Kirim-Source: kirim — Kirim-native events
{
"id": "evt_01HXYZABCDEFGHJKMNPQRSTVWX", // ULID, also in X-Kirim-Event-Id
"type": "contact.created", // see Event Catalogue
"created_at": "2026-05-23T10:00:00Z",
"livemode": true, // false when fired from a sandbox key
"data": { /* event-specific payload */ }
}
// X-Kirim-Source: meta — Meta passthrough
{
"object": "whatsapp_business_account",
"entry": [ /* exactly what Meta sent */ ]
}

message.received (text) — Meta passthrough

Section titled “message.received (text) — Meta passthrough”

Headers

X-Kirim-Source: meta
X-Kirim-Event: message.received
X-Kirim-Event-Id: wamid.HBgN…
X-Kirim-Delivery-Id: wbd_01HXYZ…
X-Kirim-Attempt: 1
X-Kirim-Signature: t=1716480000,v1=<hex>

Body (raw Meta payload, passthrough)

{
"object": "whatsapp_business_account",
"entry": [
{
"id": "<WABA_ID>",
"changes": [
{
"value": {
"messaging_product": "whatsapp",
"metadata": {
"display_phone_number": "62851176008029",
"phone_number_id": "106540352242922"
},
"contacts": [
{ "profile": { "name": "John Doe" }, "wa_id": "628111111111" }
],
"messages": [
{
"from": "628111111111",
"id": "wamid.HBgN…",
"timestamp": "1716480000",
"type": "text",
"text": { "body": "Halo, mau tanya soal pesanan" }
}
]
},
"field": "messages"
}
]
}
]
}

from here is the customer’s WhatsApp number that messaged you. The phone_number_id at entry[].changes[].value.metadata.phone_number_id (here 106540352242922) is the connected number that received the message — use this in any follow-up POST /v1/{phone_number_id}/messages call.

message.received (image with caption) — Meta passthrough

Section titled “message.received (image with caption) — Meta passthrough”

Same envelope, different messages[0]:

{
"from": "628111111111",
"id": "wamid.HBgN…",
"timestamp": "1716480000",
"type": "image",
"image": {
"caption": "Bukti transfer",
"mime_type": "image/jpeg",
"sha256": "<sha256>",
"id": "<MEDIA_ID>"
}
}

The image.id is a Meta media id scoped to this phone number. Fetch the bytes via the per-account media endpoint — kirim.dev redirects (302) to a short-lived signed CDN URL so you don’t need Meta credentials:

Terminal window
# The API returns 302 → signed CDN URL. Use -L to follow.
curl -L \
https://api.kirim.chat/v1/106540352242922/messages/wamid.HBgN…/media \
-H "Authorization: Bearer $KIRIM_KEY" \
-o transfer-proof.jpg

Headers: X-Kirim-Event: message.status

Body

{
"object": "whatsapp_business_account",
"entry": [
{
"id": "<WABA_ID>",
"changes": [
{
"value": {
"messaging_product": "whatsapp",
"metadata": {
"display_phone_number": "62851176008029",
"phone_number_id": "106540352242922"
},
"statuses": [
{
"id": "wamid.HBgN…",
"status": "delivered",
"timestamp": "1716480010",
"recipient_id": "628111111111",
"conversation": {
"id": "<META_CONVERSATION_ID>",
"origin": { "type": "user_initiated" }
},
"pricing": {
"billable": true,
"pricing_model": "CBP",
"category": "user_initiated"
}
}
]
},
"field": "messages"
}
]
}
]
}

status cycles through sentdeliveredread. Failed sends emit a single failed status with an errors[] array containing Meta’s error code — mappable to a stable kirim.dev code via the error catalogue.

Fired the moment kirim.dev queues an outbound send. Lands in milliseconds — no need to wait for Meta.

Headers

X-Kirim-Source: kirim
X-Kirim-Event: message.created
X-Kirim-Event-Id: evt_01HXYZABCDEFGHJKMNPQRSTVWX

Body

{
"id": "evt_01HXYZABCDEFGHJKMNPQRSTVWX",
"type": "message.created",
"created_at": "2026-05-23T10:00:00Z",
"livemode": true,
"data": {
"message": {
"id": "msg_01HXYZABCDEFGHJKMNPQRSTVWX",
"object": "message",
"whatsapp_account_id": "wba_01HXYZ…",
"phone_number_id": "106540352242922",
"to": "628111111111",
"type": "text",
"status": "queued",
"conversation_id": "cnv_01HXYZ…",
"created_at": "2026-05-23T10:00:00Z"
}
}
}

Headers: X-Kirim-Source: kirim, X-Kirim-Event: conversation.assigned

Body

{
"id": "evt_01HXYZABCDEFGHJKMNPQRSTVWX",
"type": "conversation.assigned",
"created_at": "2026-05-23T10:00:00Z",
"livemode": true,
"data": {
"conversation": {
"id": "cnv_01HXYZABCDEFGHJKMNPQRSTVWX",
"object": "conversation",
"status": "open",
"whatsapp_account_id": "wba_01HXYZ…",
"phone_number_id": "106540352242922"
},
"assignee": {
"user_id": "usr_01HXYZ…",
"team_id": "tem_01HXYZ…",
"previous_user_id": null
}
}
}
{
"id": "evt_…",
"type": "conversation.closed",
"created_at": "2026-05-23T10:00:00Z",
"livemode": true,
"data": {
"conversation": {
"id": "cnv_…",
"object": "conversation",
"status": "resolved",
"closed_by_user_id": "usr_…",
"phone_number_id": "106540352242922"
}
}
}
{
"id": "evt_01HXYZABCDEFGHJKMNPQRSTVWX",
"type": "contact.created",
"created_at": "2026-05-23T10:00:00Z",
"livemode": true,
"data": {
"contact": {
"id": "ctc_01HXYZABCDEFGHJKMNPQRSTVWX",
"object": "contact",
"phone_number": "+628111111111",
"name": "John Doe",
"email": null,
"metadata": null,
"whatsapp_account_id": "wba_01HXYZ…",
"phone_number_id": "106540352242922",
"created_at": "2026-05-23T10:00:00Z",
"updated_at": "2026-05-23T10:00:00Z"
},
"acquisition_source": "ctwa_ad"
}
}

acquisition_source indicates how the contact reached you:

ValueMeaning
ctwa_adClick-to-WhatsApp paid ad
ctwa_postOrganic post with WhatsApp button
organicDirect chat, no ad/post referral
broadcastReached via outbound broadcast list
apiCreated via POST /v1/{phone_number_id}/contacts
manualAdded by a teammate in the dashboard
{
"id": "evt_…",
"type": "contact.updated",
"created_at": "2026-05-23T10:00:00Z",
"livemode": true,
"data": {
"contact": {
"id": "ctc_…",
"object": "contact",
"phone_number": "+628111111111",
"name": "John Doe (updated)",
"email": "[email protected]",
"phone_number_id": "106540352242922",
"whatsapp_account_id": "wba_01HXYZ…",
"...": "..."
},
"changed_fields": ["name", "email"]
}
}

changed_fields lists only the field names that actually differ from the prior version. An empty array would be elided rather than sent — no event fires when nothing changed.