Documentation Index
Fetch the complete documentation index at: https://docs.kapso.ai/llms.txt
Use this file to discover all available pages before exploring further.
v1 webhooks are legacy. Use v2 for all new integrations.
New webhooks default to v2. Existing v1 webhooks continue to work with no breaking changes.
v1 vs v2 differences
v1 (legacy)
- Includes nested
whatsapp_config object in all payloads
- Uses
whatsapp_config_id as the primary identifier
- Event:
whatsapp.config.created for connection lifecycle
- Message structure:
message_type, content fields
- Message origin:
message.origin at message level
v2 (recommended)
- Phone-number-first with
phone_number_id at top level
- No nested
whatsapp_config object
- Event:
whatsapp.phone_number.created for connection lifecycle
- Message structure: Meta-style with
kapso extensions
- Message origin:
message.kapso.origin inside kapso object
Migration to v2
1. Check current version
Look for the X-Webhook-Payload-Version header in incoming webhooks:
app.post('/webhooks', (req, res) => {
const version = req.headers['x-webhook-payload-version'];
console.log('Webhook version:', version); // "v1" or "v2"
});
2. Update webhook handler
Before (v1):
const { whatsapp_config, conversation } = data;
const phoneNumberId = whatsapp_config.phone_number_id;
const configId = conversation.whatsapp_config_id;
After (v2):
const { phone_number_id, conversation } = data;
const phoneNumberId = phone_number_id;
const conversationPhoneNumberId = conversation.phone_number_id;
3. Handle message structure
Before (v1):
const { message } = data;
const content = message.content;
const type = message.message_type;
const direction = message.direction;
const origin = message.origin;
After (v2):
const { message } = data;
const content = message.text?.body || message.image?.caption;
const type = message.type;
const kapsoData = message.kapso;
const direction = kapsoData.direction;
const origin = kapsoData.origin;
v1 event: whatsapp.config.created
Lifecycle event fired when customer connects WhatsApp (v1 only).
For v2, use whatsapp.phone_number.created instead. See Connection detection.
Headers:
X-Webhook-Event: whatsapp.config.created
X-Webhook-Signature: <hmac-sha256-hex>
X-Idempotency-Key: <unique-uuid>
X-Webhook-Payload-Version: v1
Payload (abbreviated):
{
"whatsapp_config": {
"id": "770e8400-e29b-41d4-a716-446655440002",
"phone_number_id": "123456789012345",
"business_account_id": "987654321098765",
"customer_id": "880e8400-e29b-41d4-a716-446655440003",
"display_phone_number": "+1 (555) 123-4567"
},
"project": {
"id": "990e8400-e29b-41d4-a716-446655440004"
},
"customer": {
"id": "880e8400-e29b-41d4-a716-446655440003",
"external_customer_id": "acme-corp-123"
}
}
v1 payload examples
message.received
{
"message": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"message_type": "text",
"content": "Hello",
"direction": "inbound",
"status": "received",
"origin": "cloud_api",
"from": "+15551234567"
},
"conversation": {
"id": "770e8400-e29b-41d4-a716-446655440002",
"phone_number": "+15551234567",
"whatsapp_config_id": "880e8400-e29b-41d4-a716-446655440001"
},
"is_new_conversation": true,
"whatsapp_config": {
"id": "880e8400-e29b-41d4-a716-446655440001",
"phone_number_id": "123456789012345"
}
}
message.sent
{
"message": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"message_type": "text",
"content": "On my way",
"direction": "outbound",
"status": "sent",
"origin": "cloud_api"
},
"conversation": {
"id": "770e8400-e29b-41d4-a716-446655440002",
"whatsapp_config_id": "880e8400-e29b-41d4-a716-446655440001"
},
"whatsapp_config": {
"id": "880e8400-e29b-41d4-a716-446655440001",
"phone_number_id": "123456789012345"
}
}
conversation.created
{
"conversation": {
"id": "770e8400-e29b-41d4-a716-446655440002",
"phone_number": "+15551234567",
"whatsapp_config_id": "880e8400-e29b-41d4-a716-446655440001"
},
"whatsapp_config": {
"id": "880e8400-e29b-41d4-a716-446655440001",
"phone_number_id": "123456789012345"
}
}
Handling both versions
If you need to support both v1 and v2 webhooks during migration:
app.post('/webhooks', (req, res) => {
const version = req.headers['x-webhook-payload-version'];
const { event, data } = req.body;
if (event === 'whatsapp.message.received') {
let phoneNumberId, content, type, origin;
if (version === 'v2') {
// v2 format
phoneNumberId = data.phone_number_id;
content = data.message.text?.body;
type = data.message.type;
origin = data.message.kapso.origin;
} else {
// v1 format (legacy)
phoneNumberId = data.whatsapp_config.phone_number_id;
content = data.message.content;
type = data.message.message_type;
origin = data.message.origin;
}
// Process with normalized data
await processMessage({ phoneNumberId, content, type, origin });
}
res.status(200).send('OK');
});
Backward compatibility
v1 webhooks remain fully supported. You can migrate at your own pace:
- Existing v1 webhooks continue to work unchanged
- No breaking changes or deprecation timeline
- Both versions use the same signature verification (HMAC SHA256)
- Both versions support the same retry policy and ordering guarantees
Migrate to v2 when ready to benefit from simpler payload structure and Meta-compatible message format.