Customer.io HubSpot integration: sync the stage, not the CRM

By·Published·Updated
Customer.io HubSpot integration: sync the stage, not the CRM

In the ARPAnet era, every computer's name and address lived in one file, HOSTS.TXT, kept at SRI in California. To add or change a machine, you telephoned SRI during its business hours. Everyone else periodically downloaded a fresh copy of the file. Paul Mockapetris, recalling it for USC's Information Sciences Institute, put the problem plainly: the cost went up with every machine on the network.

Jon Postel asked Mockapetris to take on the naming problem. His answer, the Domain Name System, which the Internet Hall of Fame dates to 1983, threw out the master copy. Each organisation manages its own names on its own servers, and everyone else asks the owner when they need an answer. The first DNS server ran at ISI in 1984. By 1986, systems were shipping that relied on DNS alone and abandoned host tables entirely.

That is the right model for a CRM and a messaging tool. HubSpot is authoritative for the relationship: who the account is, what stage it has reached, which deal is open and who owns it. Customer.io is authoritative for behaviour: what people do in your product and what you send them. The mistake is copying one system's whole table into the other.

TL;DR: Customer.io's HubSpot integration brings HubSpot records into Customer.io on a schedule of 1 hour, 6 hours, 24 hours or a custom interval. Since 2 June 2026 it sets itself up from templates for contacts and companies, with AI suggesting extra fields. That makes over-syncing the default mistake. Sync the lifecycle stage, the deal stage and the handful of fields a segment, trigger or Liquid tag will actually read. Leave notes, activity counts, forecasting fields and stage-history timestamps in HubSpot. Pick one owner for the lifecycle stage, and make it HubSpot. Resyncing never deletes an attribute you have stopped syncing, and every company or deal you bring in becomes an object that counts against your profile allowance. Sync less on day one; add a field when a message needs it.

What the integration does

The integration is a data-in source. Customer.io's documentation describes it in one line: "Customer.io gets data from HubSpot through syncs." You connect HubSpot through OAuth, then set up one sync per HubSpot record type.

When a sync first runs, it brings in every record that matches its settings. After that, each run brings in records that changed since the previous interval. The getting started page lists the intervals as 1 hour, 6 hours, 24 hours or a custom interval. Nothing here is real time, which matters for any message you planned to send the moment a rep moves a deal.

Three plan facts belong at the top of any scoping conversation. The page is marked Premium and Enterprise, and it carries a Beta label. On the Customer.io pricing page, Premium starts at $1,000 a month, billed yearly, and Essentials lists only basic integrations. If you are on Essentials, the native HubSpot sync is not on your plan.

How HubSpot records land in Customer.io

HubSpot and Customer.io do not share a data model, so every sync needs a mapping. The mapping documentation gives the typical formats:

HubSpot record Typical format in Customer.io
Contacts People
Companies, Deals Custom objects
Tasks, Orders Events

The rule underneath it is simple. People receive messages and perform events. Objects cannot do either. HubSpot lets a company or a deal own activities; Customer.io does not, so any activity you bring across has to belong to a person.

That model is why account-level messaging with custom objects works for B2B. It is also why syncing every HubSpot object type costs more than it looks.

The over-syncing trap

The release note for Simplified HubSpot integration setup, shipped on 2 June 2026, made the first sync much easier. Templates for contacts and companies "automatically map common fields to Customer.io and you can use AI to suggest additional fields based on your goals." Fields the AI adds show a sparkle badge in the field list.

That is a real improvement, and it is where teams overreach. A field picker makes adding the next twenty fields feel free. It is not, for four reasons the docs spell out.

Removing a field later does not remove the data. The getting started page is explicit: "Resyncing data only adds data from HubSpot. It doesn’t delete data in Customer.io."

Objects count as profiles. The pricing page counts profiles as "people + objects". Every HubSpot company and every deal you map to a custom object becomes a profile alongside your people. Premium also caps object types at 10. Syncing companies, deals, tickets, quotes and line items as separate objects spends half of that before anyone has asked for a message.

Changes land at the next interval, not before. When you add or remove a field, the update takes effect at the next sync interval. Applying a corrected field list to records already synced takes a full resync, and a resync still only adds.

Filters have to come first. Filtering HubSpot data happens through actions on a separate Customer.io (Workspace) integration. The filtering guide recommends setting filters up before you connect HubSpot. Otherwise "you might send unwanted data to Customer.io that you’ll have to remove later!"

An unneeded field is cheap to add and slow to remove, and an unneeded object type eats into a fixed allowance. Neither shows up as an error. The docs say it in one line: "HubSpot contains a lot of data, not all of which is useful in Customer.io."

Sync this, not that

Our rule is one test per field: will a segment, an automation trigger or a Liquid tag read it? If not, it stays in HubSpot, where your sales team already looks for it. The table is the starting set we use for a B2B workspace. Property names follow HubSpot's labels from its default deal properties and lifecycle stage documentation.

Record Sync to Customer.io Leave in HubSpot
Contact Email, HubSpot record ID, first name, Lifecycle stage, contact owner, hs_marketable_status Notes, call logs, sales email history, traffic source drill-downs, Lead Status unless SDR messaging uses it
Company Name, HubSpot record ID, Lifecycle stage, industry, plan or tier if HubSpot holds it Revenue estimates, employee counts you never segment on, enrichment fields
Deal Deal stage, Pipeline, Close date, Is Closed Won, Closed lost reason, deal owner Deal probability, Weighted amount, Forecast amount, Next step, Last activity date, Number of Sales Activities
Stage history Nothing by default Date entered [stage] and Date exited [stage] properties, one pair per stage
Forms Handle as webhooks, not through this sync Form submission records

Lifecycle stage is the field that earns its place. HubSpot defines it as the property that "shows you where a specific contact or company is in your processes". Its defaults run from Subscriber to Evangelist. That one field can drive a nurture track per stage, an onboarding entry on Customer, and an exit when a lead becomes an Opportunity.

The deal stage tells you what sales is doing right now. A late-stage deal should pause nurture to its contacts. Closed lost, with a reason, starts re-engagement. Closed won starts onboarding. You need the stage and the outcome, not the forecast maths.

Forecasting fields change for reasons that are not messages. HubSpot recalculates Deal probability when a rep moves a stage, and Weighted amount follows it. These are reporting values for a sales manager. No email should read them.

Stage-history properties multiply. HubSpot creates a Date entered and a Date exited property for every lifecycle stage, default and custom. If one message needs one date, sync that one.

Marketable status is a filter. The filtering guide shows how to filter HubSpot marketing contacts on hs_marketable_status. It is a string, so "you can’t use the is true operator". Filter on the text value "true", and make sure the sync includes the field you filter on.

Forms do not belong in this sync. The docs say to handle submissions as webhooks from HubSpot instead. Our post on native and connected forms in Customer.io covers the alternatives.

Identifiers before fields

The identifier decides whether the integration works in both directions. For the contacts template, Customer.io can identify people by properties.email when email identifiers are enabled, and store the HubSpot id as contact_id. That lets Customer.io merge contacts from several sources by email.

If you use a different identifier, the mapping docs are firm: you need to store the HubSpot id as an attribute. They call it "the only way we can reliably send data back to HubSpot from Customer.io." Keep it on every person and object, even in a lean sync.

Which way data flows

Two systems writing the same field is a race, and the loser is whoever reads it.

HubSpot owns the lifecycle stage. HubSpot can set it automatically from associations. A deal's pipeline stage can update the lifecycle stages of its associated companies and contacts. With company sync turned on, a primary company's stage is applied to its contacts. Those rules live in HubSpot's lifecycle automation settings, and they only work if HubSpot is the system writing the stage.

There is a second reason not to write lifecycle stage from Customer.io. HubSpot's own documentation says its tools, the API included, can only move the default lifecycle stage forwards. To set an earlier stage with those tools, the value has to be cleared first, manually or with a workflow. Any tool that writes an earlier stage has to clear the value first, so a write-back from Customer.io adds a step nobody sees.

Customer.io owns behaviour, and sends a summary back. Sales does not need your event stream. It needs to know when an account crossed a line you agreed: activated, invited a team, went quiet. Send that as a contact or company property, or as a custom behavioural event, through the HubSpot destination.

Keep that summary small, because both systems put limits on it. Customer.io's HubSpot destination docs note that HubSpot truncates a custom behavioural event to its first 50 properties. HubSpot's API usage guidelines cap custom events at 500 unique event definitions per account and 30 million event completions per month.

Test deal updates before you rely on them. The docs disagree with themselves here. The destination's action list includes an "Upsert Custom Object Record" action, described as "Upsert records of Deals, Tickets or other Custom Objects in HubSpot." The FAQ on the same page, written about a Create Custom Object Record action, says: "We do not support record updates for custom, non-company objects; you can only create new records this way." Test a deal update in a HubSpot sandbox before you rely on either. Whatever the result, let reps or HubSpot workflows move deals, and let Customer.io read the result.

This is the same split we argue for in Customer.io attributes versus events: state that changes slowly is an attribute, and things people do are events. HubSpot holds state about the relationship. Customer.io holds the events.

Decision boundaries

How much to sync depends on who runs revenue, not on how many fields HubSpot has.

Motion HubSpot's role What to sync into Customer.io What to send back
Sales-led B2B System of record for accounts, deals and owners Contacts, companies, deals in the pipelines that matter, lifecycle stage, deal stage Product milestones as properties or behavioural events
Product-led with sales assist Where reps work the accounts that self-serve flags Lifecycle stage, contact owner, marketable status; companies only if you message at account level A product-qualified signal and the usage summary reps ask for
Product-led, marketing only Mostly a marketing database Possibly nothing; your product is the source of people Little or nothing

Sales-led B2B is where the native sync pays for itself: the deal tells you when to stop selling and start onboarding. Product-led with sales assist is where over-syncing hurts most, because your product already sends Customer.io the people and their behaviour. HubSpot adds only the human layer. If HubSpot is just a contact database your product also feeds, importing it creates a second copy of people you already have. That is the HOSTS.TXT pattern again.

When to sync more

A lean sync is a starting point, not a ceiling. Add a field when a named message needs it.

  • Account-level messages. When you start messaging every admin on an account, bring in the company fields that segment them: tier, industry, region.
  • Renewals held in HubSpot. If renewal dates live on deals or companies in HubSpot and nowhere else, sync the date.
  • Rep-assisted onboarding. If onboarding emails come from the account owner, sync the owner's name and email so Liquid can use them.
  • Other record types. Templates cover contacts and companies only. Anything else needs a custom sync, which maps only the record ID by default.

When not to use the native sync

  • You are on Essentials. The integration sits on Premium and Enterprise.
  • HubSpot is not the source of truth. If your warehouse already joins CRM and product data, send Customer.io the finished fields from there. The pricing page lists reverse ETL on every plan. Our comparison of Customer.io Data Pipelines and Segment covers how those routes fit together.
  • You need sub-hourly reactions. The shortest listed interval is 1 hour. For a message that must follow a deal change within minutes, use a HubSpot workflow webhook. HubSpot's guidelines describe webhook actions in workflows for Marketing Enterprise subscriptions, and note that those webhook calls do not count towards the API rate limit.
  • You are still choosing tools. If you are weighing Customer.io against HubSpot's own email tools, read our honest comparison of Customer.io, Braze, Klaviyo, Iterable and HubSpot first.

Teams on Salesforce face the same choices with a different connector. Our post on the Customer.io Salesforce external client app migration covers that side.

Set-up order

  1. Write the message list first. List the segments, triggers and Liquid tags that will read HubSpot data. Every field in the sync should trace to one of them.
  2. Build filters before connecting. Add a new Customer.io (Workspace) integration, set conditions on its actions, and only then connect HubSpot. Include every field your filters read.
  3. Sync contacts with the template. Accept the starting fields, then remove what fails the test.
  4. Add companies and deals as objects. Keep the HubSpot id as the object identifier unless you hold a better one from your own database.
  5. Pick the longest interval you can live with. Daily is fine for nurture by stage; hourly suits deal-driven onboarding.
  6. Agree the write-back. Name the few product signals Customer.io sends to HubSpot, and confirm that nothing in Customer.io writes the lifecycle stage.

Reviewing the field list before the first sync is part of our email marketing consulting work. For a full build, see our Customer.io agency page.

If you want help deciding what your HubSpot sync should carry, send us an enquiry or book a call with David.

Frequently asked questions

Which Customer.io plans include the HubSpot integration?

The HubSpot data-in integration is available on Customer.io's Premium and Enterprise plans and is labelled Beta in the docs. Customer.io's pricing page lists Premium from $1,000 a month, billed yearly. Essentials lists basic integrations only, so the native HubSpot sync is not part of it.

Does Customer.io sync data from HubSpot in real time?

No, it syncs on an interval. You choose 1 hour, 6 hours, 24 hours or a custom interval when you enable a sync. The first run brings in every matching record, and later runs bring in records that changed since the previous interval.

How do HubSpot companies and deals appear in Customer.io?

HubSpot companies and deals map to custom objects in Customer.io by default. Contacts map to people, companies and deals map to custom objects related to those people, and tasks and orders map to events. Objects cannot receive messages or perform events, so you message the people related to them.

What should I sync from HubSpot to Customer.io?

Sync the lifecycle stage, the deal stage and the few fields a segment, trigger or Liquid tag will read. Leave forecasting, activity counts and stage-history timestamps in HubSpot.

If I remove a field from my HubSpot sync, is it deleted in Customer.io?

No, removing a field stops future updates but leaves existing values in place. Customer.io's docs say resyncing only adds data from HubSpot and does not delete data in Customer.io.

Can Customer.io update the lifecycle stage in HubSpot?

Customer.io can send contact properties back through the HubSpot destination, but it should not own the lifecycle stage. HubSpot's documentation says its tools, the API included, can only move the default lifecycle stage forwards unless the value is cleared first.

Can Customer.io update existing HubSpot deals?

Customer.io's documentation is not consistent on whether its HubSpot destination can update existing deals. The destination lists an Upsert Custom Object Record action for deals and tickets, but the FAQ on the same page says only new records can be created for custom, non-company objects. Test a deal update in a HubSpot sandbox before relying on it. Contacts and companies can be created or updated.

Should I sync HubSpot form submissions through the integration?

No, Customer.io's docs advise against syncing forms or form submissions with this source. They recommend handling submissions as webhooks from HubSpot instead, while a better forms option is in progress.

Sources

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 →
David Crowther
Book a free consultation →

On our call, you'll be speaking with David Crowther, founder of NerveCentral.

Our initial consultation is not a sales call—you'll talk, I'll listen and ask questions—then I'll come back to you within 48 hours with our best ideas on how to grow your business.