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 (id, phone_number, phone_number_id). Optional conversation.kapso contains summary metrics (counts, last-message metadata, timestamps)
- phone_number_id - Included at top level for routing
Project webhook events
whatsapp.phone_number.created
Fires when a customer successfully connects their WhatsApp through a setup link. See Connection detection for implementation guide. Payload:workflow.execution.handoff
Fires when a workflow execution is handed off to a human agent. Payload:| Field | Description |
|---|---|
handoff.reason | Optional reason provided during handoff |
handoff.source | agent_tool (from agent step) or action_step (from workflow action) |
workflow.execution.failed
Fires when a workflow execution fails due to an error. Payload:WhatsApp webhook events
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
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:| Field | Included when |
|---|---|
pricing | Sent status, plus delivered or read |
errors | Failed to send or deliver |
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

