phone_number_id at the top level.
Payload structure
Webhook payloads separate message data from conversation data:- message.kapso - Message-scoped only: direction, status, processing_status, statuses (raw status history), origin, has_media, content (text representation), transcript (for audio), media helpers (media_data, media_url, message_type_data)
- conversation - Top-level identifiers such as
id,phone_number,phone_number_id, and, when available,business_scoped_user_id,parent_business_scoped_user_id, andusername. Optionalconversation.kapsocontains summary metrics (counts, last-message metadata, timestamps) - phone_number_id - Included at top level for routing
phone_number, from, to, or wa_id are always present.
WhatsApp webhook events
Sent only to phone-number webhooks (/whatsapp/phone_numbers/{phone_number_id}/webhooks).
Message received
whatsapp.message.receivedFired when a new WhatsApp message is received from a customer. Supports message buffering for batch delivery.Message sent
whatsapp.message.sentFired when a message is successfully sent to WhatsAppMessage delivered
whatsapp.message.deliveredFired when a message is successfully delivered to the recipient’s deviceMessage read
whatsapp.message.readFired when the recipient reads your messageMessage failed
whatsapp.message.failedFired when a message fails to deliverConversation created
whatsapp.conversation.createdFired when a new WhatsApp conversation is initiatedConversation ended
whatsapp.conversation.endedFired when a WhatsApp conversation ends (agent action, manual closure, or 24-hour inactivity)Conversation inactive
whatsapp.conversation.inactiveFired when no messages (inbound/outbound) for configured minutes (1-1440, default 60)Payload structures
Kapso sends the event name in theX-Webhook-Event header. The examples below show unbuffered request bodies. If buffering is enabled for whatsapp.message.received, the request body uses a batch envelope with type, batch: true, data: [...], and batch_info; each data item has the same shape as the unbuffered whatsapp.message.received payload below. See Batched webhook format for a full batched payload example.
whatsapp.message.received
whatsapp.message.sent
whatsapp.message.delivered
whatsapp.message.failed
whatsapp.conversation.created
whatsapp.conversation.ended
whatsapp.conversation.inactive
Multiple inactivity timeouts
Create separate webhooks for different timeout thresholds:Message origin
Themessage.kapso.origin field indicates how the message entered the system:
- cloud_api - Sent via Kapso API (outbound jobs, flow actions, API calls)
- business_app - Echoed from WhatsApp Business App (when using the Business App)
- history_sync - Backfilled during message history imports (only if project ran sync)
Status history
Themessage.kapso.statuses array contains the complete history of raw Meta status events for a message, ordered chronologically. Each entry is the unmodified payload from Meta’s webhook.
Status object structure
Each status object in the array follows Meta’s webhook format:
See Meta’s status webhook reference for the complete schema.
Use this field to track the full lifecycle of outbound messages and understand failure causes. The array only appears when status events have been recorded.
Message types
Themessage.type field can be one of:
text- Plain text messageimage- Image attachmentvideo- Video attachmentaudio- Audio/voice messagedocument- Document attachmentlocation- Location sharingtemplate- WhatsApp template messageinteractive- Interactive message (buttons, lists)reaction- Message reactioncontacts- Contact card sharing
Message type-specific data
Media messages (image/video/document)
Audio messages
Location messages
Template messages
Interactive messages
Reaction messages
Contact messages
When you send a request contact info CTA and the user taps it, WhatsApp sends acontacts message. It includes the user’s phone number and keeps BSUID identity when available.
origin is other and the payload can include a vCard.
