reverse-etl-activation
Installation
SKILL.md
Reverse ETL activation (warehouse to GTM)
The warehouse is the source of truth. This skill moves a computed audience out of it and into the tools where GTM actually happens, without corrupting those tools. The failure mode this skill exists to prevent: a naive "sync the table" job that writes half-matched rows, re-writes unchanged records, blows past API limits, and pushes suppressed or non-consented contacts into a sequence. Do it in this order every time.
Inputs (block and ask if missing)
- Source: warehouse + the exact model/table of the scored/segmented audience (e.g.
analytics.gtm.enterprise_high_intent). Confirm the grain: account, contact, or both. - Destination(s): CRM (Salesforce/HubSpot), ad platform (Meta/Google/LinkedIn), and/or sequencer (Outreach/Salesloft/Swan). Each has its own object model and limits.
- Sync key: the stable identifier used to match a warehouse row to a destination record (domain, email, CRM Id, ad-platform match key).
- Field mapping: which warehouse columns map to which destination fields, and which are read-only in the destination.
Procedure
- Resolve identity BEFORE writing. Map each warehouse account/person to a destination record on the sync key. Apply a match-confidence floor (default
0.90). Rows below the floor are quarantined to a review list, never guessed into a create. Ambiguous many-to-one matches (two warehouse rows, one CRM account) are collapsed by a deterministic rule (most-recent, highest-score), never duplicated. - Compute the change-data-capture (CDC) diff. Compare the resolved audience to the last-synced state. Only real deltas flow: new adds, changed updates (field-level, not whole-row), and intended removals. Unchanged rows never sync (this is what wrecks API budgets and audit logs).
- Map fields with type/format guards. Coerce and validate every mapped field: dates to the destination's format, enums to allowed picklist values, currency/number types, string length caps. A row that fails schema is rejected to an error list with the reason, not force-written.
- Apply suppression + consent gates. Drop anything on the do-not-contact / unsubscribe list, anything failing region/consent (GDPR/CCPA), and anything a channel-specific rule excludes (e.g. no ad-platform push for opted-out contacts). Suppression runs AFTER matching so you can log exactly who was held and why.
- Batch + schedule to the destination's limits. Chunk to each API's batch size and rate limit (default batch
200), backoff on 429s, and schedule cadence per destination (default: CRM near-real-time, ad audiences hourly, sequences gated on an explicit enrollment step, never automatic). - Dry run first. Emit the planned adds / updates / suppressions / errors with counts and a sample of each. Enforce a max-delete guard: if intended removals exceed a share of the audience (default
10%), ABORT and surface it, do not mass-delete on a bad upstream run. Require sign-off unless the run is under an auto-approve threshold. - Live sync, then reconcile. Execute, capture per-row success/failure, and produce a reconciliation report: expected vs. written counts per object, error rows with reasons, suppressed rows. Persist the new sync state so the next run's CDC is correct.