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

# CDP conversion forwarding: Meta CAPI, TikTok Events, and the multi-platform hub

> Send CDP-derived conversions to ad networks server-side — configure the dedicated Meta Conversions API and TikTok Events destinations, plus the multi-platform conversion-forwarding hub (Meta, Google, TikTok, LinkedIn, Snapchat, Pinterest, Reddit) with hashed user data, event-name mappings, and forward run history.

# CDP conversion forwarding: Meta, TikTok, and the multi-platform hub

The **Integrations → CDP** page carries three ad-measurement surfaces that push CDP-derived conversions (purchases, leads, sign-ups) back to your ad platforms as server-side events, so paid-media ROI is measurable end to end:

* **Meta CAPI** — a dedicated destination tab for Meta's Conversions API.
* **TikTok Events** — the dedicated destination tab for TikTok's server-side Events API.
* **Conversion forwarding** — a multi-platform hub covering Meta, Google, TikTok, LinkedIn, Snapchat, Pinterest, and Reddit in one place, with a forward run-history table.

All three surfaces share two guarantees:

1. Email and phone identifiers are normalized and SHA-256-hashed server-side before they leave Orbit — cleartext PII is never sent to the networks.
2. The access tokens you paste into the dedicated destination tabs are encrypted at rest and never returned by a read — the page shows a "Token set" badge and a write-only input.

## 1. What conversion forwarding does

CDP-tracked events arrive through your workspace's ingest pipeline. The forwarding surfaces map a subset of those events to each ad network's conversion shapes (a purchase with value and currency, a lead, a sign-up) and dispatch them over the network's server-side API — the same class of signal the ad platforms use for attribution and bid optimization, but measured on your server-side data rather than a browser pixel.

Each enabled platform gets its own destination id (Meta pixel/dataset id, Google customer id, TikTok pixel code) and optional event-name mappings that translate your CDP event names to the network's vocabulary (`Order Completed` → `Purchase`). Unmapped events forward under their original name, so enabling a platform without mappings still produces attribution.

Server-side dispatch means the network receives events even when the browser blocked the pixel — and because every forward is recorded (requested / matched / dispatched counts), you can verify from the dashboard that measurement is actually flowing before you trust the metrics the ad platform reports.

## 2. Meta CAPI destination

Open **Integrations → CDP → Meta CAPI**. The dedicated tab wraps `GET /api/v1/cdp/meta-capi-destination` for reads and `PATCH /api/v1/cdp/meta-capi-destination` for writes.

| Field                  | API key           | What it does                                                                                                                                                       |
| ---------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Enabled**            | `enabled`         | When on, dispatched CDP conversions forward to this destination.                                                                                                   |
| **Pixel / dataset ID** | `pixel_id`        | The Meta pixel (dataset) the events land in.                                                                                                                       |
| **Test event code**    | `test_event_code` | Optional. Route events into Meta's Test Events view in Events Manager to validate hashing before going live. Send an empty string to clear.                        |
| **Access token**       | `access_token`    | The Conversions API access token. Write-only — saved through PATCH only and encrypted at rest; the GET never returns it, so a blank field means "leave unchanged". |

Read the worker-owned status strip at the bottom of the card to verify flow: last run time, an OK / Failed / Skipped badge, the sent-count, and the consecutive-failure counter with the last error inline. `PATCH` accepts only the whitelisted keys above — anything else is ignored.

### Example

```bash theme={null}
curl -X PATCH https://api.orbit.devotel.io/api/v1/cdp/meta-capi-destination \
  -H "Authorization: Bearer $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "enabled": true,
    "pixel_id": "1234567890",
    "test_event_code": "TEST12345",
    "access_token": "EAAG..."
  }'
```

Save with the test event code set, dispatch one conversion, and confirm it appears under Test Events in Meta's Events Manager; then clear the code and leave the destination enabled for live traffic.

## 3. TikTok Events destination

Open **Integrations → CDP → TikTok Events**. The twin of the Meta tab, wrapping `GET /api/v1/cdp/tiktok-events-destination` for reads and `PATCH /api/v1/cdp/tiktok-events-destination` for writes.

| Field                       | API key           | What it does                                                                                            |
| --------------------------- | ----------------- | ------------------------------------------------------------------------------------------------------- |
| **Enabled**                 | `enabled`         | When on, dispatched CDP conversions forward to this destination.                                        |
| **Pixel / event-source ID** | `pixel_code`      | The TikTok pixel (event source) the events land in.                                                     |
| **Test event code**         | `test_event_code` | Optional. Route events into TikTok's Test Events view before going live. Send an empty string to clear. |
| **Access token**            | `access_token`    | TikTok Events API access token. Write-only, encrypted at rest, never returned by GET.                   |

### Example

```bash theme={null}
curl -X PATCH https://api.orbit.devotel.io/api/v1/cdp/tiktok-events-destination \
  -H "Authorization: Bearer $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "enabled": true,
    "pixel_code": "C1A2B3C4D5E6F7G8H9",
    "access_token": "ttk..."
  }'
```

The same run-status strip (last run, OK / Failed / Skipped, sent count, consecutive failures, last error) appears once the worker has processed a batch.

## 4. Google GA4 / Ads in scope

The Google half of the catalog covers two routes. Outbound CDP **event** forwarding to `Google Analytics 4` as a destination kind is already documented in the [CDP source and destinations guide](/guides/cdp-source-and-destinations) — that remains the canonical surface for the outbound-events half. **Conversion** measurement to Google Ads is one of the platforms on the conversion-forwarding hub below, alongside Meta, TikTok, LinkedIn, Snapchat, Pinterest, and Reddit. Pick the surface that matches the signal type: outbound event streaming goes through Destinations; conversion measurement goes through the forwardings hub.

## 5. The conversion-forwarding hub

Open **Integrations → CDP → Conversion forwarding**. This hub is the measurement half of CDP activation (the audience half pushes segments; this half pushes conversions). It renders one config card per platform and a forward-history table at the bottom.

* `GET /api/v1/cdp/conversions/config` — every platform's mapping: enabled, Nango connection id, destination id, event-name mappings, and the last-forward run status.
* `PATCH /api/v1/cdp/conversions/config/:platform` — update one platform. `:platform` is one of `facebook`, `google`, `tiktok`, `linkedin`, `snapchat`, `pinterest`, `reddit`. Only the keys you send are touched.
* `GET /api/v1/cdp/conversions/forwards` — recent forward runs across all platforms, newest first (bounded retention).
* `POST /api/v1/cdp/conversions/forward/:platform` — dispatch a batch of conversion events for one platform; each run appends to the history above.

Each platform card asks for:

* **Destination id** — the network-side target: Meta pixel id, Google customer id, TikTok pixel code, or the network equivalent.
* **Nango connection id** — the connection identifier from the OAuth flow you complete once under the Destinations tab. The hub holds no secrets — it references the connection by id only.
* **Event mappings** — `source → target` rows translating your CDP event names to the network's conversion / action names. Empty means events forward under their original name.

The card header shows the platform's **Active** / **Inactive** badge plus the last forward status when one exists (OK, Partial, Failed, or Skipped). The **Forward history** table lists recent runs across all platforms with per-run requested / matched / dispatched counts and the last error — Skipped rows mean the platform is enabled but not fully wired, not that conversions flowed.

### The save gate

The Save button on a platform card blocks when you try to enable a platform without both a destination id and a connection id — an enabled-but-unwired mapping never reaches the network (every run is logged as skipped), so the page refuses to persist it. Pre-configuring is fine: leave the platform disabled, save it, and enable once the connection is wired. The backend mirrors the guard — an enabled mapping without a destination returns `409` from the forward endpoint.

## 6. Send conversions with hashed user data

Dispatch a batch through the forward endpoint. User identifiers come in cleartext (as the pipeline receives them); the forward path hashes them server-side before dispatch — cleartext is never persisted, logged, or sent to the network.

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/cdp/conversions/forward/facebook \
  -H "Authorization: Bearer $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "events": [
      {
        "event_name": "Order Completed",
        "event_time": 1726470000,
        "event_id": "ord_9f8e1c",
        "value": 129.99,
        "currency": "USD",
        "order_id": "order_55321",
        "action_source": "website",
        "event_source_url": "https://your-store.example/checkout/confirm",
        "user": {
          "email": "customer@example.com",
          "phone": "+15551234567",
          "external_id": "user_88371",
          "fbc": "fb.1.1726469000.PQe8mV1A...",
          "fbp": "fb.1.1726468000.123456789",
          "ip": "203.0.113.9",
          "user_agent": "Mozilla/5.0 (…) ..."
        }
      }
    ]
  }'
```

Notes:

* **event\_name** — the CDP source event name; your stored mappings translate it to the platform's conversion name.
* **event\_id** — the dedup key the network uses to de-duplicate this server event against a browser-pixel event of the same id. Always send it when a client-side pixel also fires.
* **user** — at least one matchable identifier is required: `email`, `phone`, `external_id`, a mobile advertising id, or the network click id (`fbc`, `gclid`, `ttclid`, `li_fat_id`, `sc_click_id`, `pinterest_click_id`, `rdt_cid`). The click + context fields pass through raw because the networks match them unhashed — hash them yourself and matching falls to zero.
* A batch is capped at 1,000 events per request. The network itself only accepts a batch once the platform config is wired (`destination_id` + `nango_connection_id`); otherwise the forward returns `409` and the run history records the skip.

## 7. Guard-rails against double-fire

Three independent controls keep conversions from being counted twice or forwarded without a basis:

* **Event-level dedup** — the auto-forward loop claims a deterministic idempotency key per event (scoped to workspace, destination, event name, and the CDP event id) for 48 hours, so a redelivered message or a worker retry resolves to the same key and is skipped rather than re-POSTed. Platform-side, the `event_id` you attach is the dedup key the network applies between its server events and browser-pixel events.
* **Consent-aware suppression** — an EU subject who has not granted ad-data consent gets the conversion suppressed outright (no legal basis), while a US state-privacy opt-out is downgraded to Meta Limited Data Use rather than dropped — the forward happens, flagged, instead of vanishing silently.
* **Daily volume cap** — each workspace's auto-forward volume is bounded by a per-day cap (a generous platform default; a workspace can lower it from settings) so a runaway event source can't inflate attribution with a flood of forwards. The guard fails open on dependency degradation — a guard-rail can never silently drop a legitimate forward.

On the measurement hub side, the analogous guard is the save gate from section 5: an enabled mapping without a destination id or a connection id refuses to persist, and the forward endpoint returns `409` instead of dispatching — runs are recorded as skipped in the history table either way, so a broken wiring is loud rather than invisible.

## 8. Role gating

Mutations on all three surfaces — the Meta CAPI and TikTok destination configs and the conversion-forwarding hub — are gated to **owner, admin, or developer** roles. Members and viewers read but cannot change the measurement configuration.

## See also

* [CDP integration console: source and destinations](/guides/cdp-source-and-destinations) — the outbound event-forwarding half (Google Analytics 4 included)
* [CDP segments](/guides/cdp-segments) — build the audiences conversions are attributed against
* [CDP tracking plan](/guides/cdp-tracking-plan) — declare the conversion event shapes producers send
* [CDP event debugger and DLQ](/guides/cdp-event-debugger-and-dlq) — tail the inbound stream that feeds forwarding
