> ## 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.

# Legacy v1 webhooks

> Migration guide for v1 webhook payloads

<Warning>
  v1 webhooks are legacy. Use v2 for all new integrations.
</Warning>

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:

```javascript theme={null}
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)**:

```javascript theme={null}
const { whatsapp_config, conversation } = data;
const phoneNumberId = whatsapp_config.phone_number_id;
const configId = conversation.whatsapp_config_id;
```

**After (v2)**:

```javascript theme={null}
const { phone_number_id, conversation } = data;
const phoneNumberId = phone_number_id;
const conversationPhoneNumberId = conversation.phone_number_id;
```

### 3. Handle message structure

**Before (v1)**:

```javascript theme={null}
const { message } = data;
const content = message.content;
const type = message.message_type;
const direction = message.direction;
const origin = message.origin;
```

**After (v2)**:

```javascript theme={null}
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](/docs/platform/setup-links/detect-connection).

**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):

```json theme={null}
{
  "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

```json theme={null}
{
  "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

```json theme={null}
{
  "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

```json theme={null}
{
  "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:

```javascript theme={null}
app.post('/webhooks', (req, res) => {
  const version = req.headers['x-webhook-payload-version'];
  const event = req.headers['x-webhook-event'];
  const 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.
