Customer.io warehouse sync: Snowflake audiences without a CDP
In 1953 an IBM salesman, R. Blair Smith, sat next to C. R. Smith, the head of American Airlines, on a flight from Los Angeles to New York. They noticed they shared a surname and got talking about reservations. At the time, agents kept bookings on handwritten cards. Seat counts moved between offices by telephone and teletype, so they drifted, and flights went out oversold or undersold.
Blair Smith told him he "was going back to study a computer that had the possibility of doing more than just keeping availability". It could hold each passenger's name, itinerary and phone number. American Airlines and IBM announced the joint project on 5 November 1961. It ran on two IBM 7090 mainframes and cost about $40 million. Both IBM and the Engineering and Technology History Wiki put it at 7,500 reservations an hour. A booking went from tens of minutes to seconds.
The speed was not the point. The point was one live record per customer that every office read from, instead of copies passed down a phone line. Most Customer.io teams already have that record. It is a table in Snowflake or BigQuery. The real question is how many copies of it you pay to keep.
TL;DR: Customer.io builds audiences from Snowflake and BigQuery data. Its reverse ETL integrations run your SQL on a schedule, as often as every minute, and import each row as a person, an event, an object or a relationship. Segments then work on that imported copy. Customer.io's pricing page lists reverse ETL as included on every plan, from Essentials at $100 a month. If your audiences already live in the warehouse, sync them straight in and skip the CDP. A CDP earns its place only when more than two tools need the same identity graph. And if a message has to go out seconds after something happens, sync nothing: send the event directly.
Customer.io imports warehouse data, it does not query it
Customer.io's documentation says its reverse ETL features let you "bring in data from your data warehouse without a separate tool" (Customer.io, reverse ETL). You write a query, Customer.io runs it on the interval you choose, and each returned row becomes an API call.
Customer.io does not segment inside your warehouse. Every docs page on the feature describes the same mechanism: run the query on a schedule, then import the results. The Databricks release note of 25 September 2026 puts it plainly: "Customer.io runs the query on that schedule and imports the data, so you can segment your audience".
So segments, triggers and Liquid all read Customer.io's copy, not your tables. Freshness is capped by your sync interval, and every object you import counts towards your profile allowance.
Once the data lands, people enter and exit data-driven segments "automatically when they match and stop matching the conditions that you set for the segment". Our advanced segmentation guide for Customer.io covers the condition logic.
How the sync works
A reverse ETL integration has two parts: a connection to your warehouse and one or more syncs.
The connection. Customer.io recommends a read-only user with access to only the tables you sync. For Snowflake, you authenticate with a private key in PKCS#8 PEM format, and the queries need a virtual warehouse to run on. The docs' example creates an XSMALL warehouse with AUTO_SUSPEND = 600, which is ten minutes. For BigQuery, you create a service account with the BigQuery User and BigQuery Job User roles and paste its key.
The syncs. Each sync is one call type (identify, track, group and so on), one query and one frequency. Columns become traits or properties. People and their purchases are two syncs, not one.
The incremental filter. Customer.io exposes a {{last_sync_time}} variable: a Unix timestamp for when the previous successful sync started. Compare an indexed "last updated" column against it and each run only sends changed rows. On the first run the value is 0, so you sync everything. Two details catch people out:
- The value stays
0until a sync completes. If every run ends in Some Rows Failed, you keep re-sending the whole table on every interval. - Customer.io deduplicates on a
messageId. Repeated operations are ignored for 12 to 24 hours, and events get a second check against the last 30 days. If you setuserIdas themessageId, you are limited to one update per person per window.
Overlaps. If a sync is still running when the next one is due, Customer.io skips the next run.
Warehouse quirks. Snowflake returns column names in capitals, so EMAIL must become "email" with AS or the identifier will not match. BigQuery does not support dot notation for nested fields, so you build nested objects with json_object. BigQuery DATETIME values need converting with UNIX_SECONDS before Customer.io treats them as timestamps.
None of this is new. The Snowflake import shipped on 14 December 2021, and objects and relationships followed on 7 March 2023. Databricks support shipped on 25 September 2026. The integrations documentation now lists it beside Amazon Redshift, BigQuery, Microsoft SQL, MySQL, PostgreSQL and Snowflake.
The original database syncs are now labelled "These are legacy integrations" in the legacy reverse ETL overview. New accounts only see the newer ones. The newer integrations feed the Pipelines API, and the docs note "You can also send the data you sync on to other services downstream." So yes, Customer.io Data Pipelines has its own reverse ETL source. For the wider map, see our guide to integrating data sources with Customer.io.
What you can sync
| Your warehouse table holds | Sync type | Lands in Customer.io as | Typical use |
|---|---|---|---|
| Traits and scores: plan, LTV, churn risk, region | identify |
Person attributes | Data-driven segments, Liquid |
| Things that happened: orders, renewals, cancellations | track |
Events | Segments on event history, slower triggers |
| Accounts, subscriptions, venues, courses | group |
Objects and relationships | Account-level segments and messages |
| Deletions and suppressions | track with reserved names |
Deletes or suppressions | Keeping the workspace clean |
A few rules from the docs are worth knowing before you write the SQL:
- An
identifysync creates a new person for anyuserIdthat does not exist. Add_updateto the query if you only want to update people you already have. - A
groupsync creates the object, and the person, if either is missing. IncludeobjectTypeId, or Customer.io assumes object type 1. A person's role on an account goes inrelationshipAttributes. - There is no delete sync type. You send events with reserved names instead:
User Deleted,Object Deleted,Relationship Deleted,Suppress PersonandUnsuppress Person.
The group sync is where warehouse sync earns its keep for B2B teams. Your accounts table, with its seat counts and renewal dates, becomes objects that people relate to. That is the foundation for account-level messaging with Customer.io custom objects.
Limits that shape the design
These come from the reverse ETL limits table and the automation triggers page.
| Limit | Value | What it means in practice |
|---|---|---|
| Shortest sync frequency | 1 minute | Near-real-time at best, never instant |
| Records per sync | 40 million | Counts rows extracted, not rows changed |
| Columns per sync | 300 | Wide tables need splitting |
| Record size as JSON | 512 KiB | Huge JSON blobs fail |
| Deduplication window | 12 to 24 hours | Resyncs need a saved sync definition |
| Object or relationship trigger fan-out | 1,000 people per update | Larger accounts trigger nobody |
The Snowflake and BigQuery pages add one piece of advice that matters more than any number above. Do not run queries returning millions of rows more than once a day, because it can delay automations and messages. Big tables sync daily. Small, filtered views can sync often.
What it costs
There are three bills: Customer.io, your warehouse and the CDP.
Customer.io. The pricing page lists Reverse ETL as included on Essentials, Premium and Enterprise. Essentials starts at $100 a month for 5,000 profiles, and the page defines profiles as "people + objects". Extra profiles cost $0.009 each. Essentials allows 2 object types against 10 on Premium. Data warehouse destinations, which send data back out to the warehouse, are not included on Essentials.
Two caveats. The pricing page splits integrations into Basic and Premium without saying which list each warehouse sits in, so check yours in the workspace directory before you budget. And objects cost money. As a worked example, syncing 20,000 account rows as objects adds 20,000 profiles. If your people already fill the 5,000 included profiles, that is $180 on a monthly Essentials bill at $0.009 each. That is our arithmetic, not a quote.
Your warehouse. Snowflake bills warehouses per second, with a 60-second minimum each time one starts. An X-Small Gen1 warehouse uses 1 credit an hour while it runs. The docs' example warehouse suspends after 600 seconds idle, so:
- A sync every minute never lets it suspend. That is 24 credits a day.
- An hourly sync keeps it awake for at least 10 minutes each hour. Snowflake's own table puts 10 minutes at 0.167 credits, so about 4 credits a day.
- A daily sync costs roughly 0.167 credits a day, plus the query time.
Those totals are our arithmetic from Snowflake's published rates. Sync frequency is a billing decision too.
The CDP. Twilio Segment's Connections pricing starts the Team plan at $120 a month for 10,000 visitors, with 1,000,000 Reverse ETL records. The plan table adds $12 per extra 1,000 monthly tracked users from 10K to 25K, $11 from 25K to 100K and $10 above that. The FAQ on the same page says a flat $10, so check the figure with sales.
The catch is what that buys. The same table offers identity resolution and profiles only with CDP plans, and the CDP plans are quote-only: "Contact us for plan and add-on pricing." The published Segment price buys the pipe. The audience building you would be buying it for sits on a plan with no published price.
To make the gap concrete: at 50,000 monthly tracked users, the Team table works out at $120 + 15 × $12 + 25 × $11 = $575 a month. That is our arithmetic, and it is before any CDP plan.
Count the tools that need one identity
Count the tools that need to agree on who a person is.
| Your situation | Do this | Why |
|---|---|---|
| Audiences and scores already modelled in Snowflake or BigQuery, and Customer.io is the main tool acting on them | Warehouse sync | One copy of the truth, one integration, no extra licence |
| Three or more tools (messaging, ads, analytics, support) need the same resolved person, including anonymous visitors stitched to known users | A CDP | Identity resolution upstream of every tool is the job a CDP is for |
| A message must go out seconds after a product action | Send events directly | A batch sync adds at least a minute plus query time |
| Data changes rarely and only matters in aggregate | Warehouse sync, daily | Cheapest on both bills |
If you are in the first row, your data team already builds the churn score, the plan tier and the account health in dbt or plain SQL. Adding a CDP means paying to rebuild those audiences in a second tool, then keeping two definitions of active customer in step. The warehouse sync sends the definition you already trust.
The CDP case is real, but it is about identity, not audiences. If your ad platforms, your product analytics and Customer.io all need the same anonymous-to-known stitching in real time, a CDP does work the warehouse cannot. Below that threshold it is a second copy. We made the wider case for consolidating on Customer.io's own pipeline in Customer.io Data Pipelines vs Segment.
The third row is the one people miss. A cart abandonment or an onboarding nudge should come from an event your app sends the moment it happens. If you are unsure which data belongs where, our post on Customer.io attributes vs events draws that line.
When not to do this
Warehouse sync is the right default for audiences. It is the wrong tool in five situations.
Real-time triggers. The shortest interval is one minute, plus query time, plus any skipped overlap. Password resets, receipts, cart abandonment and in-session onboarding all need events sent directly from your app or server.
Switching it on next to live automations. The first run imports every row because last_sync_time starts at 0. The attribute or segment trigger, launched in April 2025, does not care how old the data is. The triggers documentation says "backfilled users could start the automation right away". Run the first sync with dependent automations paused, or add time-based conditions to the trigger.
Backfilling event history later. An event sync only captures events after the last sync time, so you cannot backfill by editing the query. The docs' answer is a separate backfill sync that you run once and then disable.
Large accounts as triggers. If one object update fans out to more than 1,000 related people, none of them enter the automation. Segment on object attributes instead and trigger from the segment.
Arrays you want to segment on. Customer.io's segments documentation says attribute values "are only simple strings" for segmentation purposes. Flatten arrays into separate columns or booleans in the SQL.
How we set one up
The connection is quick. The order around it is what makes it hold:
- Agree the identifier first. The
userIdin the warehouse must be the same ID your app sends. For leads without an ID yet, the Snowflake docs showCOALESCE(CAST(USER_ID AS VARCHAR), EMAIL) AS "userId", which casts the ID to text and falls back to email. Write the naming rules down, the same way you would for an event schema for marketing teams. - Build one narrow view per sync. Only the columns Customer.io uses, lower-case names, and an indexed
last_updatedcolumn. - Give Customer.io its own read-only user and its own X-Small warehouse, so you can see its cost and revoke it cleanly.
- Run the first sync with automations paused. Check the Imports tab and fix failed rows until a sync completes and
last_sync_timeis non-zero. Then build segments and switch automations on. - Set each frequency to the slowest your use case tolerates. Daily for scores, hourly for plan changes, events directly for anything urgent.
Step 1 is the one that bites later. A warehouse sync built on a mismatched ID does not fail loudly. Every identify row with an unknown userId creates a new person, so you get a second copy of everyone.
Have the data in Snowflake or BigQuery already and want it working in Customer.io without another tool in the middle? That is the build we do as a Customer.io agency. Tell us about your set-up and we will tell you which of the three routes fits.
Frequently asked questions
Does Customer.io support warehouse-native audience building from Snowflake or BigQuery?
Customer.io builds audiences from Snowflake and BigQuery data by importing it, not by querying the warehouse at send time. Its reverse ETL integrations run your SQL on a schedule, as often as every minute, and turn each row into a person, event, object or relationship. Segments then update from that copy.
How often can Customer.io sync data from Snowflake?
Customer.io can run a Snowflake sync as often as every minute. If a run is still going when the next is due, the next is skipped. The docs advise against running queries that return millions of rows more than once a day, because it can delay automations and messages.
Is reverse ETL included on the Customer.io Essentials plan?
Customer.io's pricing page lists Reverse ETL as included on Essentials, Premium and Enterprise. Essentials starts at $100 a month for 5,000 profiles, counting people and objects together. Data warehouse destinations, which send data back to the warehouse, are not included on Essentials.
Does Customer.io Data Pipelines have a reverse ETL source?
Customer.io Data Pipelines does have a reverse ETL source. The current reverse ETL integrations send each row through the Pipelines API, so the data can go on to other destinations as well as Customer.io. The older database syncs are marked as legacy integrations.
Do I need Segment to get warehouse data into Customer.io?
You do not need Segment to get warehouse data into Customer.io, because Customer.io connects to Snowflake, BigQuery, Redshift, Databricks, PostgreSQL, MySQL and Microsoft SQL directly. A CDP such as Segment earns its place when three or more tools need the same resolved identity.
Can a warehouse sync trigger a Customer.io automation in real time?
A warehouse sync cannot trigger an automation in real time, because the shortest sync interval is one minute and query time adds to that. Synced data can still trigger automations minutes or hours later. For anything that must go out seconds after a product action, send the event directly from your app or server.
Do synced objects count towards my Customer.io profile limit?
Synced objects count towards the Customer.io profile limit, because the pricing page defines profiles as people plus objects. Essentials includes 5,000 profiles and charges $0.009 for each extra one, so a large accounts table can use up a small plan's allowance on its own.
Sources
- Customer.io, Understanding reverse ETL integrations, documentation, updated 2 October 2026.
- Customer.io, Snowflake reverse ETL, documentation, updated 2 October 2026.
- Customer.io, Google BigQuery reverse ETL, documentation, updated 14 September 2026.
- Customer.io, Reverse ETL overview (legacy integrations), documentation, updated 14 September 2026.
- Customer.io, Integrations documentation index, read 5 October 2026.
- Customer.io, Import people from Snowflake, release note, 14 December 2021.
- Customer.io, Import objects and relationships in reverse ETL integrations, release note, 7 March 2023.
- Customer.io, Databricks support, release note, 25 September 2026.
- Customer.io, How segments work, documentation, updated 14 September 2026.
- Customer.io, Triggers, filters, and frequencies, documentation, updated 14 September 2026.
- Customer.io, Pricing and plans, read 5 October 2026.
- Twilio Segment, Connections pricing, read 5 October 2026.
- Twilio Segment, Customer Data Platform pricing, read 5 October 2026.
- Snowflake, Overview of warehouses, documentation, read 5 October 2026.
- IBM, Sabre, IBM Heritage, read 5 October 2026.
- Engineering and Technology History Wiki, SABRE Airline Reservation System, read 5 October 2026.
Want this working in your Customer.io workspace?
It's what we do all day for SMBs—strategy, automations, deliverability and hands-on execution.
See how we work as a Customer.io agency →
