The reverse-ETL destination model
Reverse-ETL is one of the four export families — 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 onGET /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 is the decision).
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 asprofile_export_capability on GET /api/v1/cdp/reverse-etl/profile-exports:
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: truerequires anativeverdict on a configured and enabled destination, and a trait inactivestatus.- Otherwise
syncable: falsewith an explicitblock_reason:destination_not_configured,destination_disabled,traits_via_exported_data(the entireorganictier), ortrait_not_active(paused traits keep their last materialised value; archived is terminal).
- 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.
- 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.
- Treat the warehouse copy as eventually consistent. When freshness matters for a downstream model, read the destination’s
last_run_aton 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 atGET /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—okorfailed.rows_contacts/rows_events(withrows_totalas 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.
failing— the most recent run returnedfailed; 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.”
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.
6. When reverse-ETL is the wrong tool
- A one-off audience pull — pick the ad-hoc CSV family; standing up a destination for a single CSV wastes the destination’s schedule.
- Retention evidence / audit — pick the WORM archival family; 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 — the four export surfaces and the shared safety guarantees.
- CDP segment recompute model — the trait-recompute cadence the export cadence composes with.
- Reverse-ETL warehouse exports guide — configuring destinations, setting the cadence, and syncing now.
- Tenant isolation — the per-tenant schema boundary every push respects.