> ## 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 reverse ETL and data share: the operator walkthrough

> Operate every reverse-ETL surface on the CDP integrations page — warehouse-as-source imports, the destinations panel, the per-warehouse editor, segment and profile exports, field mappings, and sync-run history — plus the governed interaction data share for raw calls, messages, and conversations.

# CDP reverse ETL and data share

The CDP integrations page (`/integrations/cdp`) carries two reverse-ETL
families and one data-share family, and each family owns a distinct pipeline:

* **Sources (reverse-ETL)** — pull warehouse rows *into* the CDP event stream
  (Postgres today; BigQuery, Snowflake, and Databricks connectors route the
  same emit path).
* **Reverse-ETL destinations** — push CDP contacts, events, and computed
  profile traits *out* to a warehouse you own (BigQuery, Snowflake, Redshift,
  Postgres, Databricks, ClickHouse).
* **Interaction data share** — push Orbit's own core interaction records
  (calls, messages, conversations) into an Iceberg/S3, BigQuery, or Snowflake
  warehouse under a versioned schema contract.

This guide walks each surface end to end, in the order an operator uses them on
the page: the source import first, then the outbound destinations, then the
data-share tab. For the outbound push destination model, read the
[reverse-ETL destination model](/concepts/reverse-etl-destination-model); for
the per-destination setup recipes, read the
[reverse-ETL warehouse exports guide](/guides/reverse-etl-warehouse-exports).
This page is the operational map — which tab owns what.

## 1. What reverse ETL does (the Sources tab)

The **Sources (reverse-ETL)** tab answers the *inbound* direction: pull rows
out of a warehouse you own and emit them into your CDP event stream, so the
warehouse becomes a first-class CDP source next to your SDK and HTTP endpoints.

Each source row carries a name, a connector kind, an encrypted connection
string, a parameterised SQL query, and a schedule:

* **Connector kinds** — `postgres` (self-managed) is the direct-connection
  path; `bigquery`, `snowflake`, and `databricks` route through API-driven
  connectors that share the same emit + cursor-advance path as Postgres.
* **The SQL query** is a read-only `SELECT` with a single `{cursor}`
  placeholder. The scheduler binds the placeholder as a parameter, and the
  service layer rejects non-SELECT statements, so the query can never write.
* **`cursor_column`** names the watermark column the scheduler advances between
  runs.
* **`event_name`** is the CDP event type each row lands under (default
  `warehouse.row`).
* **`schedule_minutes`** sets the poll cadence (5 minutes to 24 hours; default
  hourly).
* **Status** is `active`, `paused`, or `errored`.

Create a source from the tab or the API:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/cdp/sources \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "billing-product-events",
    "kind": "postgres",
    "connection": "postgres://etl_reader:***@host:5432/analytics",
    "sql_query": "SELECT user_id, event, amount FROM product_events WHERE updated_at > {cursor} ORDER BY updated_at LIMIT 5000",
    "cursor_column": "updated_at",
    "event_name": "product.charge",
    "schedule_minutes": 60
  }'
```

The connection string is stored encrypted at rest and never returned by any
read; `GET /api/v1/cdp/sources` returns only the non-secret shape. Pausing is a
confirm-gated action in the tab because it silently halts the import; deleting
is confirm-gated the same way.

## 2. Destinations panel (`GET /cdp/reverse-etl/destinations`)

The **Destinations panel** on the page answers the outbound question in one
table: which reverse-ETL destinations are wired, which are enabled, and which
are healthy. It consumes the aggregated read model
`GET /api/v1/cdp/reverse-etl/destinations` — one query for the whole
destination overview instead of one config GET per destination.

Each row shows the destination name and category from the connector catalog, a
state badge (Enabled / Paused / Not configured), a credentials-presence flag,
and the derived health. A row carries a **Sync now** action only when the
destination is configured and enabled — the server advertises that gate per
row, so a disabled destination can never receive a sync request.

Use the panel to answer "which destinations are wired?" at a glance; drill into
the per-warehouse editor (next section) to change a destination's settings.

## 3. Warehouse editor (Snowflake + Redshift, sibling to BigQuery)

The **Reverse ETL** config cards edit each destination's connection.
`GET /api/v1/cdp/reverse-etl` owns the BigQuery destination;
`GET /api/v1/cdp/reverse-etl/warehouse/:warehouse` owns the non-Google
warehouses — `snowflake`, `redshift`, `postgres`, `databricks`, `clickhouse`.

For Snowflake, the editor asks for the account, warehouse name, role, database,
schema, table prefix, cadence, and an RSA key pair. For Redshift and Postgres,
host, port, database, schema, and credentials. The credential field is
write-only: `GET` never returns it, the editor shows a "configured" badge when
one is stored, and leaving the field blank keeps the existing secret. Saves are
explicit, and identifier validation runs server-side.

For the full Snowflake key-pair and BigQuery service-account recipes, use the
[reverse-ETL warehouse exports guide](/guides/reverse-etl-warehouse-exports).

## 4. Segment exports (`GET /cdp/reverse-etl/segment-exports`)

The **Segment exports** card answers the segment side: which of your segments
sync membership into which warehouse destination. It consumes the aggregated
read model `GET /api/v1/cdp/reverse-etl/segment-exports`.

Every destination row carries a capability verdict — `native` (first-class
segment-membership propagation), `organic` (reachable via SQL over the exported
rows), or `not_configured` — and every blocked segment carries an explicit
`block_reason`, so you always see *why* a segment is not propagating instead of
silently missing it. The card is read-only; destination credentials are edited
on the sibling config cards.

## 5. Profile exports (`GET /cdp/reverse-etl/profile-exports`)

The **Profile exports** card is the trait-side twin: which of your computed
traits propagate with the exported contacts entity into which destination. It
consumes `GET /api/v1/cdp/reverse-etl/profile-exports` and renders the same
capability verdicts as the segment card — `native` traits land as first-class
columns on BigQuery, Snowflake, Redshift, and Postgres; `organic` destinations
(Databricks, ClickHouse) expose the trait data via SQL over the exported rows
instead. A paused or archived trait shows its `block_reason` rather than
vanishing.

## 6. Field mappings editor and sync-runs history

The **Field mappings** card edits the optional per-table exclude / rename map
applied row-by-row before push. It writes the `field_mappings` key on the
BigQuery config (`PATCH /api/v1/cdp/reverse-etl`); the `id` column is locked so
the dedup key always survives.

The **Sync-runs** card surfaces the latest export run for every configured
warehouse destination in one table, consuming
`GET /api/v1/cdp/reverse-etl/sync-runs`. Per destination you see the last-run
timestamp, row counts (`rows_contacts` / `rows_events`), a derived health badge
(`ok`, `failing`, `stale`, `never_run`, or `disabled`), the last-error detail,
and two on-demand actions — **Sync now** (run the normal forward delta at the
next scheduler tick) and **Backfill** (replay history from an optional
instant) — both gated on a configured + enabled destination. Health is derived
server-side and rendered as-is.

PATCH the BigQuery config with a field-mappings block directly:

```bash theme={null}
curl -X PATCH https://api.orbit.devotel.io/api/v1/cdp/reverse-etl \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "enabled": true,
    "field_mappings": {
      "contacts": { "exclude": ["internal_notes"], "rename": { "phone": "msisdn" } },
      "cdp_events": { "exclude": ["ip"] }
    }
  }'
```

## 7. How errors surface

Failures never stay silent. A destination's read surfaces carry a sanitized
`last_error` code — a coarse enum such as `config_or_credential`,
`tenant_client_open`, or `export_stream` — never the raw driver message, so the
error panel is safe to share with an audit channel. The Sources tab surfaces
its own sanitized source-side codes through the same mapping. Watch the
`consecutive_failures` count on the sync-runs read; the dashboard paints a
warning once it exceeds zero.

## 8. Interaction data share (raw interaction records)

The **Data share (warehouse)** tab is a distinct family from reverse ETL.
Where the destinations above fan out CDP **audiences, segments, events, and
profiles**, this tab shares the platform's own **core interaction records** —
calls, messages, and conversations — into your warehouse under a governed,
**versioned schema contract**, with a historical-backfill request.

* **Schema contract** — `GET /api/v1/cdp/interaction-data-share/schema-contract`
  lists every published contract version and the exact field list per dataset
  (`calls`, `messages`, `conversations`). Fields are never removed or retyped
  within a version once published; a new field only ever appears in a new
  version, so a warehouse model pinned to an older version keeps working.
* **Targets** — `GET/PATCH /api/v1/cdp/interaction-data-share/:provider` for
  `iceberg_s3`, `bigquery`, or `snowflake`. The PATCH writes connection
  identifiers, the datasets to share, the governing contract version, and the
  encrypted credential (Iceberg REST-catalog token or AWS secret access key,
  service-account JSON, or Snowflake password / private key). The credential is
  encrypted at rest and never returned; the tab shows a "configured" badge.
* **Backfill** — `POST /api/v1/cdp/interaction-data-share/:provider/backfill`
  requests a one-off historical backfill for a subset of the configured
  datasets over a date range. Only one backfill is in flight per provider, and
  the request is rejected unless the target is enabled and fully configured.

Config, validation, the schema-contract registry, and the backfill request are
live today; the scheduled egress worker that carries the rows advances the
`last_run_*` / backfill status fields the tab renders read-only.

Pick **data share** when you need raw interaction records (calls, messages,
conversations) for your own BI or data-science layer. Pick **reverse ETL** when
you need the CDP's modeled entities — contacts, events, segments, and computed
profile traits.

## 9. Admin path versus DPO

The reverse-ETL and data-share surfaces are *config* surfaces: the fields they
write (connection identifiers, cadences, dataset lists, contract versions) are
tenant-owned controls, and the secrets they accept are stored encrypted and
never returned. That is the surface a normal admin path needs.

A data-protection review cares about a different question — *where does
personal data live before it fans out to a destination?* Route that review to
the **Data catalog** and **Erasure propagation** surfaces, which classify per
property and set per-destination delete-versus-suppress policy. Keep the
reverse-ETL tabs for the wiring itself; do not use them as the audit surface.

## Related guides

* [Reverse ETL to Snowflake and BigQuery](/guides/reverse-etl-warehouse-exports)
  — the per-destination setup recipes and cadence semantics.
* [The reverse-ETL destination model](/concepts/reverse-etl-destination-model)
  — the capability verdicts the segment/profile exports cards render.
* [CDP clean rooms](/concepts/cdp-clean-room-model) — the governed sharing
  surface with zero data egress, sibling to data share.
* [Export families model](/concepts/export-families-model) — the export-safety
  guarantees every family inherits.
* [API reference — CDP endpoints](/api-reference/endpoints/cdp) — the
  per-operation shapes for every endpoint named above.
