CRM Migration · Two-Way Sync

Moving 200,000 records from Salesforce to HubSpot — without breaking the business.

The company ran on Salesforce and Pardot. Marketing needed HubSpot. Sales couldn’t leave Salesforce. The answer was a live two-way sync — but only after fixing what would have broken it.

200K
Salesforce records migrated with two-way sync live
2 mo
To full sales team adoption post go-live
Zero
Business disruption — weekend cutover, operational same day
Salesforce → HubSpot Migration Two-Way Sync B2B SaaS · Global GTM team Salesforce · HubSpot · Pardot Anonymised

The Situation

Two teams. Two systems. One source of friction.

The company had built its sales operation on Salesforce. The pipeline was there, the lead assignment was there, the deal history was there. Sales was not moving.

But marketing needed HubSpot — for email automation, lifecycle tracking, nurture programs, and attribution that Pardot was no longer delivering well enough at their scale. The ask was not a rip-and-replace. It was a co-existence: HubSpot for marketing, Salesforce for sales, with a live two-way sync keeping both in agreement.

The database had grown to roughly 200,000 records — Leads, Contacts, Accounts, and Opportunities accumulated across years of global sales activity. Before a single record could move, the state of that data needed to be understood. And what the audit found made it clear: moving first would have broken the sync on day one.

Before — The Salesforce Audit

Clean before you connect. Always.

The first rule of any migration is that the quality of what comes out is determined entirely by the quality of what goes in. Before touching the HubSpot integration, a full Salesforce data audit was run across all objects.

Issue FoundDetail
Duplicate leadsSame prospect appearing multiple times — different email formats, manual re-entries, import overlaps
Missing required fieldsKey properties blank across large portions of the database — no owner, no country, no lead source
Records owned by inactive usersDeactivated Salesforce users still listed as record owners — a critical sync blocker (see below)
Test and QA dataTest leads, QA accounts, and internal team email addresses mixed into the live database
Inactive picklist valuesSalesforce picklist fields contained inactive values that HubSpot would reject during sync
Unconverted leadsLeads that had progressed past qualification but were never formally converted to Contact + Account + Opportunity

Each of these issues was resolved before the HubSpot integration was configured. Not after. The cleanup was the migration — the technical sync was just the final step.

The Critical Pre-Flight Check

The inactive owner problem — and why it had to be fixed first.

Of all the issues found in the audit, inactive record ownership was the most dangerous for a two-way sync. It is also the most commonly overlooked.

⚠ How this breaks the sync

When a Salesforce user is deactivated, any records still assigned to them become a sync liability. The records will sync from Salesforce into HubSpot without issue. But when HubSpot attempts to write an update back to Salesforce — a property change, a lifecycle stage update, a marketing activity — Salesforce rejects the write because the assigned owner no longer exists as an active user. The sync becomes one-directional without anyone realising it.

This happens most commonly when sales reps leave the company and IT deactivates their Salesforce accounts without reassigning their records. In a database of 200,000 records accumulated over several years, this was not an edge case — it was widespread.

Every record owned by an inactive user was identified and reassigned to an active Salesforce user before the integration was enabled. This single step prevented the majority of sync errors that would otherwise have appeared immediately after go-live.

“A two-way sync that only works in one direction is not a sync. It is a data divergence waiting to happen. Inactive owner cleanup is non-negotiable before you connect any CRM to HubSpot.”

The Object Model Decision

Salesforce has Leads and Contacts. HubSpot only has Contacts.

This structural difference is one of the most important things to resolve before a Salesforce-HubSpot migration — and it is often the thing teams think least about.

In Salesforce, the Lead object and the Contact object are distinct. A record begins as a Lead. When a sales rep qualifies it and converts it, Salesforce automatically creates three linked records: a Contact, an Account (Company), and an Opportunity. The original Lead record is marked as converted.

Many companies skip the Lead object entirely and go straight to Contact + Account. This company used the Lead object fully — their sales process ran through Lead conversion as the formal qualification gate.

HubSpot does not have a Lead object. Every record — whether it is a Salesforce Lead or a converted Salesforce Contact — lives as a Contact in HubSpot. This creates an immediate question: how do you know, from within HubSpot, whether a given Contact is still a Lead in Salesforce or has already been converted?

Salesforce
  • Lead (pre-conversion)
  • Contact (post-conversion)
  • Account (Company)
  • Opportunity (Deal)

Lead ID & Contact ID on every record
Two-way
sync
HubSpot
  • Contact (all records)
  • Company
  • Deal

CRM Record Type property

The solution was a custom HubSpot property: CRM Record Type. This property was populated automatically via workflow based on which Salesforce ID was present on the synced record. If only a Salesforce Lead ID existed, the record was tagged as “Lead.” If a Salesforce Contact ID was present, it was tagged as “Contact” — indicating the lead had been converted in Salesforce.

This property became the foundation for segmentation, automation logic, and reporting inside HubSpot. Marketing workflows could now behave differently depending on whether they were addressing a prospect still in qualification or a converted contact already in an active deal.

The Sync Architecture

Two systems. One source of truth per field.

The most important decision in any two-way sync is establishing which system “wins” when both have data for the same field. Without clear rules, conflicts accumulate silently and the sync becomes unreliable.

The guiding principle here was straightforward: Salesforce owns everything the sales team touches. HubSpot owns everything marketing creates.

Field CategoryPreferred SourceRationale
Lead owner / assignmentSalesforceLead assignment logic lived entirely in Salesforce — changing owner in HubSpot was disabled
Sales StageSalesforceSales-owned field — only editable from Salesforce, read-only in HubSpot
Lead StatusSalesforceSales activity tracking — source of truth remained with the sales team
Deal / Opportunity propertiesSalesforceDeal progression managed by sales in Salesforce
Marketing engagement dataHubSpotEmail opens, form fills, page views, lifecycle stage — all owned by HubSpot
Lifecycle stageHubSpotDriven by marketing automation and lead scoring
CRM Record TypeHubSpotCustom property populated by HubSpot automation based on SF ID presence

One additional constraint applied to picklist fields. Salesforce has an “inactive picklist value” feature that HubSpot does not support. Any picklist value marked inactive in Salesforce will be rejected by HubSpot during sync. All dropdown values across both systems were audited and aligned before the integration went live — no mid-flight corrections were needed after go-live.

The Governance Model

Sales updated two fields. Automation handled everything else.

One of the most common failures in CRM migrations is that the new system immediately accumulates the same bad habits as the old one. Reps are asked to update too many fields. Fields get skipped. Data quality degrades within weeks.

The design here was intentionally minimal. On the Contact and Lead objects, the sales team was asked to manually update exactly two properties — both dropdowns with fixed values:

Sales-Owned Dropdown — Contact / Lead Object
Sales Stage
  • Trying to Connect
  • Discovery
  • Qualification
  • Disqualified
Sales-Owned Dropdown — Contact / Lead Object
Lead Status
  • Reflects the current state of sales activity on the record
  • Values aligned between Salesforce and HubSpot to prevent picklist sync errors
  • Updated by the rep as outreach activity progresses

Everything else — lifecycle stage progression, CRM Record Type, marketing engagement data, routing logic — was handled entirely by automation. Sales reps were not asked to interpret or maintain anything outside of these two fields.

On the Deal object, sales updated additional properties to reflect deal progression, qualification details, and outcomes. But even there, the principle held: every property that could be automated was automated. Single-line text fields were kept to a minimum across both objects. Dropdowns were preferred because they constrain input, enable workflow logic, and produce clean reportable data.

Why dropdowns instead of text fields

Free-text fields produce inconsistent data. “Trying to connect,” “trying to reach,” “initial outreach” and “TTC” are all the same status — but they cannot be grouped in a report or used to trigger a workflow. Dropdowns enforce consistency at the point of entry, which means every downstream report, workflow, and sync rule works reliably without data cleaning.

HubSpot workflows were also set up to act as a governance layer — regularly checking whether records at a given Sales Stage had the expected Lead Status value set. Where they did not, an automated task or internal notification was created to prompt the rep to update. The system enforced its own hygiene without requiring manual audits.

The Exclusion Rule

Disqualified leads stop syncing — and disappear after a week.

Not every record belongs in both systems permanently. One of the most operationally useful decisions made during this migration was building a clean exit path for bad data identified by the sales team.

When a sales rep marked a lead as Disqualified in Salesforce, the record was tagged with a disqualification reason — Bad Data, Spam Lead, Test Lead, or Team/Internal Email. Once that tag was applied and synced to HubSpot, a specific set of rules activated:

1
Sync back to Salesforce is suppressed. The record remains in HubSpot but no further updates are pushed back to Salesforce. It exits the two-way sync loop.
2
The record is excluded from all active marketing workflows. It will not receive emails, enter nurture sequences, or affect any engagement metrics.
3
After seven days, the record is automatically deleted from HubSpot. It does not accumulate. It does not inflate the database. It is gone cleanly.

This meant the two-way sync was not just a technical bridge — it was a self-cleaning data governance system. Bad data identified by sales in Salesforce automatically removed itself from HubSpot within a week. No manual list management. No periodic cleanup projects. The database stayed clean by design.

The Go-Live

Weekend execution. Zero business disruption.

With the Salesforce cleanup complete, picklist values aligned, inactive owners reassigned, and the HubSpot property architecture built and tested in the Salesforce sandbox environment, the go-live was planned for a weekend — deliberately outside business hours to eliminate any risk to active sales operations.

Pre Go-Live
Sandbox Validation
  • Full sync configuration tested against Salesforce sandbox (test.salesforce.com) before touching production
  • Field mappings validated, sync direction rules confirmed, picklist alignment verified
  • HubSpot properties built and workflow logic confirmed in production HubSpot (no HubSpot sandbox used — configuration was carefully staged)
  • Disqualified lead exclusion rules tested end-to-end
Go-Live Day
Phased Sync Across a Single Day
  • HubSpot-Salesforce integration enabled on the weekend to avoid disrupting active sales workflows
  • 200,000 records synced in phases across the day — not a single bulk flip
  • Pardot and HubSpot ran simultaneously for approximately two hours during the cutover window
  • Pardot connector was cleanly disconnected once HubSpot sync was confirmed stable
  • Sync error log monitored in real time throughout the day
Post Go-Live
Adoption and Stabilisation
  • Sales team trained on the two-property update model — minimal change to their existing Salesforce workflow
  • HubSpot used by marketing from day one for email, lifecycle automation, and attribution
  • Sync error rate was minimal from the start — a direct result of the pre-migration cleanup
  • Full sales team adoption reached within two months of go-live
  • Governance workflows active from week one — ensuring data quality held post-migration

The Key Insight

The migration was not the hard part. The cleanup was.

“Most migration projects fail not because the integration is complex, but because the data going into it was never ready. Clean data migrates cleanly. Messy data migrates messily — and then you have two systems full of the same problems, connected by a live sync that propagates every inconsistency in real time.”

The technical work of connecting Salesforce to HubSpot is well-documented and relatively straightforward. What is not straightforward is the discipline to clean before you connect — to fix inactive owners, resolve duplicates, align picklist values, and settle the object model question before a single record moves.

1
Audit before you integrate. The state of your Salesforce data determines the quality of your HubSpot data. There is no shortcut through this step.
2
Fix inactive owners before enabling sync. This single issue causes more two-way sync failures than any other. It is invisible until it is not.
3
Decide the preferred source per field, not per system. Some fields belong to Salesforce. Some belong to HubSpot. Clarity on this upfront prevents silent data conflicts later.
4
Minimise what sales has to manually update. The fewer fields reps are responsible for, the more consistently those fields get updated. Automate everything that can be automated.
5
Build the exit path for bad data into the sync itself. A disqualification rule that auto-removes records is worth more than any periodic cleanup project.

What This Delivered

Outcomes

🔄
200K records migratedSalesforce Leads, Contacts, Accounts, and Opportunities all syncing live to HubSpot with two-way data flow from day one.
Zero business disruptionWeekend cutover meant no interruption to active sales workflows. The team arrived Monday morning to a working system.
👥
Full adoption in 2 monthsSales team fully operational on the new model within two months — supported by minimal change to their existing Salesforce workflow.
Minimal sync errors at go-livePre-migration cleanup meant the integration launched cleanly. Inactive owners, duplicate records, and bad picklist values were resolved before the first sync ran.
🧹
Self-cleaning databaseDisqualified lead exclusion rule removes bad data from HubSpot automatically within seven days of sales flagging it — no manual cleanup required.
📊
Marketing + Sales finally in syncHubSpot owns marketing data. Salesforce owns sales data. Each team works in their preferred system — with a live sync keeping both accurate.

Planning a Salesforce–HubSpot migration?

The technical integration is the easy part. The audit, the cleanup, and the sync architecture are where migrations succeed or fail. That is the work I do.

Book a free 20-min HubSpot teardown →

Fixed scope. Fixed price. You will know exactly what needs to happen before committing to anything.