> ## Documentation Index
> Fetch the complete documentation index at: https://docs.switchbord.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Business Messaging CAPI

> Configure dry-run Meta Business Messaging Conversions API events for CTWA attribution.

Switchbord can record Meta Business Messaging Conversions API events for Click-to-WhatsApp conversations. The flow is intentionally ledger-first and disabled-by-default:

1. A WhatsApp inbound referral captures `ctwa_clid` from `messages[].referral.ctwa_clid`.
2. Switchbord stores the click id on the inbound message payload and `conversation_attributions`.
3. Channel-level dataset bindings decide whether a CTWA `Lead`, `QuoteRequested`, or `Purchase` event is eligible.
4. A `meta_capi_events` ledger row is created with a deterministic `event_id`.
5. The worker processes `meta.capi.dispatch` and posts to `/{dataset_id}/events` only when the binding is enabled and not in dry-run mode.

## Dataset binding

Bindings are channel scoped, not global. Use the app API route below from an authenticated owner/admin context:

```bash theme={null}
curl -X POST \
  /api/settings/channels/<channel-id>/meta-capi-binding \
  -H 'content-type: application/json' \
  -d '{
    "datasetId": "<events-manager-dataset-id>",
    "pageId": "<optional-page-id>",
    "enabled": true,
    "dryRun": true,
    "testEventCode": "TEST123",
    "allowedEventNames": ["Lead"]
  }'
```

Keep `dryRun=true` until Events Manager shows test events with the expected payload shape. Production dispatch (`dryRun=false`) is rejected unless a `testEventCode` is present and the existing binding has been marked verified (`health_status='ok'` with `last_verified_at`).

## Event ids

Switchbord uses deterministic ids for deduplication:

* `Lead`: `meta-capi:lead:{workspaceId}:{conversationId}:{attributionId}`
* `QuoteRequested`: `meta-capi:quote:{workspaceId}:{reservationTravioId}:{attributionId}`
* `Purchase`: `meta-capi:purchase:{workspaceId}:{reservationTravioId}:{confirmationTimestamp}`

The database also enforces `unique (workspace_id, event_id)`.

## Payload rules

Every dispatch payload uses:

* `action_source: business_messaging`
* `messaging_channel: whatsapp`
* `partner_agent: switchbord`
* `user_data.ctwa_clid` when available
* `user_data.page_id` when configured
* hashed phone/external id only; do not store or send raw phone numbers
* `custom_data.value` and `custom_data.currency` for quote/purchase value when present

Access tokens are always sent in the `Authorization: Bearer ***` header, never as URL query parameters.

## Rollout

1. Configure binding with `enabled=true`, `dryRun=true`, and a test event code.
2. Replay or wait for a CTWA inbound with `ctwa_clid`.
3. Confirm `meta_capi_events.status = 'dry_run'` and payload preview has no token.
4. Send Events Manager test events.
5. Flip one channel to `dryRun=false` for `Lead` only.
6. Enable `QuoteRequested` and `Purchase` only after Travio/status-3 source-of-truth integration is verified.

Rollback is instant: set `enabled=false` or `dryRun=true` on the channel binding.
