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

# The reverse-ETL destination model

> How Orbit's reverse-ETL family classifies each warehouse destination — native profile-trait columns (BigQuery, Snowflake, Redshift, Postgres), organic SQL-resolved traits (Databricks, ClickHouse), or not_configured — and how per-destination run-status (failing / stale / never_run) answers 'did my data actually land downstream?'

# The reverse-ETL destination model

Reverse-ETL is one of the four [export families](/concepts/export-families-model) — the one that continuously replicates your contacts entity, your CDP event stream, and your computed profile traits into a warehouse you own. The families page gives you the map; this page is the destination catalog itself: what the three capability verdicts mean on `GET /api/v1/cdp/reverse-etl/profile-exports`, how the push cadence interacts with trait recompute, and how per-destination run-status on `GET /api/v1/cdp/reverse-etl/sync-runs` surfaces failures instead of failing silent.

## 1. Where reverse-ETL sits in the export families map

Reverse-ETL is the **scheduled, continuous** export — the only family whose value is repetition rather than the artifact:

* The ad-hoc CSV and the conversation vCon produce a one-time download; reverse-ETL produces a replicated table inside your warehouse that stays current on a cadence.
* WORM archival proves records have not changed; reverse-ETL rewrites your downstream copy so it keeps matching the live one.
* Pick reverse-ETL when your BI, ML, or finance tooling should query Orbit data where you already keep the rest of your analytics. Pick a different family when the question is one file, one thread, or one audit (the §1 table in the [export families model](/concepts/export-families-model#1-the-four-families-and-when-each-is-the-right-move) is the decision).

The shipped catalog is six destinations: **BigQuery, Snowflake, Redshift, Postgres, Databricks, and ClickHouse**. You configure and enable each under your CDP destinations panel, and per-destination secrets (service-account JSON, connection credentials) are stored encrypted and never returned by any read surface — read surfaces expose only non-secret config state and run-status fields.

## 2. The destination catalog: three capability verdicts

Not every destination treats your computed profile traits the same way downstream. The profile-exports read model classifies each configured destination into exactly one of three verdicts, and reports it as `profile_export_capability` on `GET /api/v1/cdp/reverse-etl/profile-exports`:

| Verdict          | Destinations                            | What lands downstream                                                                                                                                                                                        |
| ---------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `native`         | BigQuery, Snowflake, Redshift, Postgres | The exported contacts entity includes the materialised `traits` column, so your computed traits propagate as first-class data and profiles resolve on the exported identifiers.                              |
| `organic`        | Databricks, ClickHouse                  | The destination carries the same contacts + events raw export, but there is no dedicated profile pipeline for it — trait data is reachable via SQL over the exported rows instead of as first-class columns. |
| `not_configured` | any of the six                          | No stored destination config exists — the verdict is null until connection setup completes.                                                                                                                  |

Each destination row on the response joins that verdict against your computed-trait catalog, so per trait you get a propagation decision, not just a tier:

* `syncable: true` requires a `native` verdict on a configured **and** enabled destination, **and** a trait in `active` status.
* Otherwise `syncable: false` with an explicit `block_reason`: `destination_not_configured`, `destination_disabled`, `traits_via_exported_data` (the entire `organic` tier), or `trait_not_active` (paused traits keep their last materialised value; archived is terminal).

Two consequences worth planning against:

* **This is a capability model, not a second pipeline.** The verdict is derived from destination config plus the trait catalog you already have — it cannot drift from what the exporters actually write. Query it once per destination instead of inferring per-destination behavior from a spreadsheet.
* **A trait that never propagates is surfaced with its reason, not absent.** Paused and archived traits still appear in the per-destination rows with `block_reason: "trait_not_active"`, so a trait you paused stops moving downstream visibly rather than vanishing from the panel.

## 3. Schedule semantics: a cadence, not a request

Reverse-ETL pushes run **on a schedule per destination**, not on demand — the schedule is the whole point of the family:

* **Default cadence is \~nightly.** Unconfigured intervals inherit the pre-feature gap of 23 hours, with scheduled runs firing only after 02:00 UTC so the previous day's upstream writes have settled.
* **The interval is yours to trade off.** Set a per-destination sync interval from 1 hour up to 30 days. Destinations at the daily-or-slower end keep the 02:00-UTC settle window; hourly-or-faster destinations run around the clock.
* **"Sync now" exists but does not change the model.** A sync-now request makes the destination due at the next scheduler tick — it brings the normal forward delta early rather than changing what gets exported, and a failed run keeps the request set so it retries.

How the cadence interacts with computed-trait recompute: computed traits re-derive on their own pipeline (per the [segment recompute lifecycle](/concepts/cdp-segment-recompute-model#section-3--scheduled-re-enrichment-of-profile-traits) — the daily enrichment sweep is the slowest ingredient). A scheduled export carries the **materialised** trait value at run time. Two operators' rules follow:

1. **Warehouse freshness is bounded by the export cadence, not the trait pipeline.** Setting a 1-hour export interval does not make daily-enriched traits hourly; it ships the current materialised value more often.
2. **Treat the warehouse copy as eventually consistent.** When freshness matters for a downstream model, read the destination's `last_run_at` on the sync-runs surface rather than assuming the hourly interval held.

## 4. Failure bookkeeping: per-destination run-status

Each successful or failed run writes its own per-destination run-status back onto that destination's config state, and the unified read surface at `GET /api/v1/cdp/reverse-etl/sync-runs` projects the latest snapshot for all six destinations in one response. Per destination row:

* `last_run_at` — when the most recent run ran.
* `last_run_status` — `ok` or `failed`.
* `rows_contacts` / `rows_events` (with `rows_total` as a convenience sum) — exported row counts per table.
* `last_error` + `last_error_at`, `consecutive_failures` — the failure detail.
* `health` — a derived single-glance verdict, resolved most-actionable-first: `disabled` → `never_run` → `failing` → `stale` → `ok`.

The two loud verdicts exist so a broken export is never silent:

* **`failing`** — the most recent run returned `failed`; the row carries the last error message and even a nightly scheduler hiccup is visible in one query instead of buried in per-destination config.
* **`stale`** — the last run succeeded but is older than 48 hours. The 48h window leaves one full missed-night of slack against the default 23h cadence, so the flag means "the scheduler genuinely stopped firing," not "last night's run is late."

The response's `summary` block rolls this up (`enabled`, `failing`, `stale`, `needs_attention`) so a dashboard or alert can key on one boolean. A destination you paused on purpose reports `health: "disabled"`; a brand-new one reports `never_run` until its first run lands.

## 5. Guarantees (inherited from the families model)

Reverse-ETL inherits the safety model every export family shares — three guarantees, stated once on the families page and linked here rather than restated:

* **Tenant-scoped.** Every push exports documents from your own tenant schema; a cross-tenant export is not something the API can do.
* **Per-record ownership checks.** A record outside your workspace never enters the export.
* **Time-bounded, signed URLs where applicable.** Any generated artifact URL is time-bounded — mirror the data into your own store rather than relying on the link staying live.

Both read surfaces above are read-only and return no secret material — the verdict and run-status are projections over your destination config, never the credentials themselves.

## 6. When reverse-ETL is the wrong tool

* **A one-off audience pull** — pick the [ad-hoc CSV family](/concepts/export-families-model#2-what-the-format-guarantees); standing up a destination for a single CSV wastes the destination's schedule.
* **Retention evidence / audit** — pick the [WORM archival family](/compliance/archival-export); reverse-ETL keeps your downstream copy current, it does not prove a record has not changed.
* **Sub-minute freshness** — pick a streaming event sink (the CDP event stream's always-on delivery) rather than the warehouse cadence, which deliberately tolerates single-night staleness before flagging `stale`.

## Cross-references

* [Export families model](/concepts/export-families-model) — the four export surfaces and the shared safety guarantees.
* [CDP segment recompute model](/concepts/cdp-segment-recompute-model) — the trait-recompute cadence the export cadence composes with.
* [Reverse-ETL warehouse exports guide](/guides/reverse-etl-warehouse-exports) — configuring destinations, setting the cadence, and syncing now.
* [Tenant isolation](/concepts/tenant-isolation) — the per-tenant schema boundary every push respects.
