CDPGuidesSupport
SourcesReverse ETL

Reverse ETL

Sync tables, SQL query results, and files from S3, SFTP, Snowflake, and other data systems into Ours Privacy visitor profiles and events on a daily schedule or on demand.

BetaThis is in beta. The basics are stable, and more capabilities are on the way.

Reverse ETL syncs data that already lives in your warehouse, data lake, or file drops into Ours Privacy. Each row becomes a visitor profile update, an event, or both, so offline outcomes such as appointments, orders, and account changes join the same journey as your website activity.

Use it when the data you want to act on is already modeled somewhere else and you do not want to build a second integration just to get it into your CDP.


What you can connect

Files

  • Amazon S3: sync CSV or JSON files from a bucket you own. Ours Privacy reads with a scoped role you grant, and never writes to your bucket.
  • SFTP: sync files from your own SFTP server, or from a vendor drop such as an EHR export. Each file is tracked so partial uploads and replaced files are handled safely.

Warehouses and data lakes

  • Snowflake: sync a full table or the result of a SQL query.
  • Data lake tables: sync open table formats in your data lake through a query engine you already run.

APIs

  • HTTP APIs: pull paginated collections from an internal service or a vendor API using cursor or offset pagination.

Need something else? Databricks, BigQuery, Redshift, Postgres, and other sources are available on request. Contact your account representative and tell us where your data lives.


How a sync works

Every reverse ETL source follows the same shape. The pieces you configure are the connection, what to read, and a transform that maps each row.

Sync a table

Point the source at a table and Ours Privacy reads every row. Use this when the table is already shaped the way you want: one row per customer, or one row per outcome.

Sync a SQL query

Write a query instead of naming a table when you need a filter, a join, or a computed column. The query runs in your warehouse, so your joins, deduplication, and business logic stay where they already live.

SELECT customer_id, email, plan, updated_at
FROM customers
WHERE active = true

Sync a file from S3 or SFTP

Point the source at a path or filename pattern. Ours Privacy reads each matching file and remembers which files it has already processed, so a daily drop only imports what is new.

Schedule it, or sync now

Each source runs on a daily schedule. You can also trigger a sync on demand from the dashboard or the API when you need fresh data before the next scheduled run.


Transforms

A transform is a short TypeScript function that maps one row to visitor updates and events. It runs in an isolated sandbox with no network access, so it can only shape the data it is given.

export default defineTransform((row) => ({
  visitors: [
    {
      externalId: String(row.customer_id),
      email: row.email,
      userProperties: { custom_properties: { plan: row.plan } },
    },
  ],
  events: [
    {
      event: 'Appointment Completed',
      externalId: String(row.customer_id),
      distinctId: `appointment-completed:${row.appointment_id}`,
      time: Number(row.completed_at_ms),
    },
  ],
}));
  • Return {} to skip a row. Useful for filtering out test accounts or rows that are not ready yet.
  • Give events a stable ID. A distinctId built from your own record ID means re-running a sync never creates duplicates.
  • Past event times are allowed up to 365 days. Backfill history by passing the original timestamp; rows with older times are rejected, so filter them out in the transform.

Look up more of your data

When a row needs context from another table, join it in the SQL query or add the column to the export rather than reaching for it in the transform. The transform sees every column the query or file returns, so a wider query is the simplest way to give it more to work with.

Look up your Ours Privacy visitor data

Rows can be matched to visitors that Ours Privacy already knows about. Look up by email, external ID, or user ID and the matching visitor's ID and selected properties are attached to the row before it reaches your transform. A miss returns nothing and creates nothing, so you can decide whether to create a new profile or skip the row.

Use this to tie an offline outcome back to the website session that drove it, or to only sync rows for people who have visited your site.


TypeScript, Python, and the SDK

  • Transforms are TypeScript. Write them in your editor with full types from the Ours Privacy SDK, then preview the output against a sample file or query result locally before the first sync runs.
  • Custom connectors in TypeScript or Python. If your data lives somewhere the built-in connectors do not reach, the connector SDK lets you write a reader in either language. Ours Privacy reviews and hosts custom connectors, so they run with the same isolation and limits as the built-in ones.
  • Configure and trigger by API. Source definitions and on-demand syncs are available through the Platform API, so you can manage reverse ETL from your own deployment pipeline.

Need help?

Reverse ETL is in beta. Contact your account representative to turn it on for your account, or email support@oursprivacy.com with questions about a specific source.


Next steps

How is this guide?

On this page