Skip to main content
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
  • 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

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;
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;

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",
    "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"
  },
  "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;

    if (version === 'v2') {
      // v2 format
      phoneNumberId = data.phone_number_id;
      content = data.message.text?.body;
      type = data.message.type;
    } else {
      // v1 format (legacy)
      phoneNumberId = data.whatsapp_config.phone_number_id;
      content = data.message.content;
      type = data.message.message_type;
    }

    // Process with normalized data
    await processMessage({ phoneNumberId, content, type });
  }

  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.