Salesforce to HubSpot Migration: Object Mapping, Migration Paths, Load Order and Cutover
Salesforce to HubSpot migration explained: how each Salesforce object maps, the connector against a scripted load, the load order, and how to verify cutover.
Paul Maxwell, PhD
AUTHOR
GET WEEKLY REVOPS INSIGHTS
No spam. Unsubscribe anytime.
On the morning after a Salesforce to HubSpot migration, the first reconciliation compares HubSpot's contact count with Salesforce's leads plus contacts, finds it far lower, and starts a search for records that were never meant to arrive. The deals pass their count check, and their defect goes unexamined: a share arrive with a company and no contact, because Salesforce kept that link on a separate record that some opportunities never had. HubSpot's revenue attribution credits revenue only through a deal's contacts, so that revenue leaves the report and nothing marks its absence.
This article explains how the Salesforce data model maps onto HubSpot and how to move it with both gaps measured first. It starts with the object map and the association rule that decides whether a deal keeps its contacts, then recomputes both on a sample org. Migration paths, rebuilt configuration and the sharing model follow, then a checklist ending in reconciliation. Symptoms, costs and returns, and the reverse direction close it.
Four terms carry the argument. The connector is HubSpot's Salesforce integration, a continuous two-way sync that can also import existing Salesforce records. Smart Transfer is HubSpot's one-way transfer tool for moving another CRM's data into a portal. A scripted migration extracts from Salesforce and writes to HubSpot's APIs under the migrating team's control. An Opportunity Contact Role is the Salesforce record linking a contact to an opportunity; in HubSpot that link is an association, stored on neither record.
The Salesforce Object Model in HubSpot Terms
The third column names the condition that decides whether each move is faithful.
| Salesforce | HubSpot destination | Condition or loss |
|---|---|---|
| SalesforceAccount | HubSpot destinationCompany | Condition or lossCompany sync on, and accounts loaded, before opportunities |
| SalesforcePerson Account | HubSpot destinationContact | Condition or lossMapped to a contact by Smart Transfer |
| SalesforceContact | HubSpot destinationContact | Condition or lossEmail is the deduplication key |
| SalesforceLead | HubSpot destinationContact | Condition or lossUnconverted leads only; a converted lead is a read-only copy |
| SalesforceOpportunity | HubSpot destinationDeal, in one pipeline per record type | Condition or lossThe connector carries currency amounts as numbers, unconverted |
| SalesforceOpportunity Contact Role | HubSpot destinationDeal-to-contact association | Condition or lossThe only documented source under the standard deal sync and Smart Transfer |
| SalesforceTask, Event, Email Message | HubSpot destinationTask, meeting, email | Condition or lossLoaded once: an import cannot update an existing activity |
| SalesforceCase | HubSpot destinationTicket | Condition or lossCarried by the connector and by Smart Transfer |
| SalesforceCustom object | HubSpot destinationCustom object | Condition or lossEnterprise only, created in HubSpot first |
| SalesforceRecord type | HubSpot destinationPipeline or property | Condition or lossPipeline count capped by subscription |
| SalesforceFormula field | HubSpot destinationCalculation property | Condition or lossNot transferred by Smart Transfer |
| SalesforceValidation rule | HubSpot destinationProperty rule, stage property or pipeline rule | Condition or lossOne property per rule; no cross-field formula |
| SalesforceApex trigger, flow | HubSpot destinationWorkflow or custom code action | Condition or lossRebuilt by hand on every path |
| SalesforceOrg-wide defaults, role hierarchy, sharing rules | HubSpot destinationOwner-scoped and team-scoped permissions | Condition or lossNo compositional equivalent |
Two rows collapse two Salesforce structures into one: the person, treated here, and a deal's link to its people, treated next. Lead conversion creates an account, a contact and optionally an opportunity, moves the lead's activities onto them, and leaves the lead as a read-only record. HubSpot holds one contact per person, matched on email by its import tool. Loading converted leads beside contacts writes each converted person twice onto one email address, and whichever file loads second overwrites the first. The extract should take only leads whose IsConverted field, documented on the lead, is false.
Opportunity Contact Roles and Association Coverage
Both documented native paths derive a deal's contacts from one Salesforce structure. The connector's standard deal sync associates a deal with a contact only where the contact is linked to the opportunity as an OpportunityContactRole, and Smart Transfer's Salesforce mapping names Opportunity Contact Roles as the source of its contact-to-deal associations. A contact held in a custom lookup field, or not recorded at all, yields a deal with a company and no contact.
The finding follows, and any org can check it before a record moves. On those two paths, the share of opportunities with at least one contact role is the ceiling on deal-to-contact coverage, and their closed-won amount is the ceiling on what revenue attribution can see. HubSpot's attribution documentation states that revenue on a deal without an associated contact is attributed to no interaction; the attribution reporting article covers the other exclusions.
One newer option is documented only in outline. The connector's updated deal sync, published as a beta on 17 September 2026, adds an association setting with a Primary association field selector, and its documentation does not list which opportunity fields that selector offers. Switching to it cannot be reversed. An org whose buying contacts sit in a custom lookup should test in a sandbox whether that lookup appears there before treating the contact-role ceiling as final.
One query measures the ceiling: SELECT COUNT() FROM Opportunity WHERE Id NOT IN (SELECT OpportunityId FROM OpportunityContactRole), run once for all opportunities, then with SUM(Amount) in place of COUNT() and the filter restricted to closed-won opportunities in the period. Where the buying contacts are known in Salesforce, the fix belongs there, because both documented paths will then carry the roles unaided. Otherwise contacts are associated in HubSpot by import after the load, under their own association label, which needs Professional or Enterprise, so that a contact attached by rule stays visible as an assertion about who bought.
Sample Org Arithmetic: Expected Counts and the Coverage Ceiling
The figures here are sample data for an invented org, chosen so that each can be recomputed; they describe no client. The org holds 14,000 accounts, 40,000 contacts, 50,000 leads and 12,000 opportunities. Of the contacts, 800 lack an email and 2,000 repeat another contact's email. Of the leads, 18,000 are converted; of the 32,000 unconverted, 1,500 lack an email, 3,000 share a contact's email and 500 repeat another lead's.
| Measure | Salesforce | Expected in HubSpot |
|---|---|---|
| MeasureAccounts to companies | Salesforce14,000 | Expected in HubSpot14,000 |
| MeasureContacts with a distinct email | Salesforce40,000 contacts | Expected in HubSpot37,200 |
| MeasureUnconverted leads adding a new email | Salesforce50,000 leads | Expected in HubSpot27,000 |
| MeasureLeads and contacts with no email | Salesforce2,300 | Expected in HubSpot0 or 2,300, by decision |
| MeasurePeople in total | Salesforce90,000 records | Expected in HubSpot64,200, or 66,500 with the email-less |
| MeasureOpportunities to deals | Salesforce12,000 | Expected in HubSpot12,000 |
| MeasureDeals with at least one contact | Salesforce9,000 with a contact role | Expected in HubSpot9,000 at most, 75% |
| MeasureClosed-won, last four quarters | Salesforce$14.4M on 2,400 opportunities | Expected in HubSpot$10.2M attributable at most, 70.8% |
The naive target of 90,000 people exceeds the email-keyed 64,200 by 25,800: the 18,000 converted leads, 3,000 leads matching a contact, 500 repeated leads, 2,000 repeated contacts and 2,300 records without email. Written down beforehand, that subtraction is a target; unwritten, it is 25,800 phantom losses.
The coverage ceiling is fixed before anything moves. Of 2,400 closed-won opportunities, the 1,700 with a contact role hold $10.2M and the 700 without hold $4.2M, so revenue attribution can describe no more than 70.8% of the period's closed-won revenue.
Volume is the last check. A connector contact sync can use up to four Salesforce API calls per record, so 72,000 unconverted leads and contacts can draw up to 288,000 calls from the org's allocation. A script sends 100 inputs per batch request, so the 90,200 people, companies and deals need 902 requests: under a minute at the 190 requests per ten seconds a private app receives on Professional.
Migration Paths: HubSpot's Salesforce Connector, Smart Transfer and a Scripted Load
| Path | What it carries | What constrains it |
|---|---|---|
| PathConnector import | What it carriesLeads, contacts, accounts, opportunities, tasks, a campaign, cases, events and custom objects | What constrains itProfessional or Enterprise; a continuous sync; people outside an inclusion segment arrive with email only |
| PathSmart Transfer | What it carriesStandard objects, users and email templates, then attachments and campaign lists | What constrains itSuper Admins only; lookup, formula and encrypted fields unsupported; custom field mappings need Data Hub |
| PathScripted load | What it carriesAnything the Salesforce API returns | What constrains itBuilt, tested and owned by the migrating team |
The connector import suits an org that will run both systems for a period. Opportunity import creates no deals until deal sync is on, and deals lose their company unless company sync is on and accounts are imported first. The sync runs until switched off, which suits the side-by-side arrangement in the HubSpot Salesforce integration article and becomes a liability once Salesforce is retired.
Smart Transfer is one-way and can be reverted, but its Salesforce mapping lists no custom objects and skips reference, calculated and encrypted fields. Apart from the account, contact and contact-role links it converts into associations, every lookup and formula field in the org stays behind.
A scripted load carries what the native paths leave behind: formula values stored as numbers, custom lookups turned into labelled associations, and a deterministic key on every record. It is safe to rerun when built on a batch upsert keyed on a unique property holding the Salesforce record ID, so that a repeated run updates rather than duplicates.
Record Types, Validation Rules, Apex and Flows
Salesforce record types vary business processes, picklist values and page layouts by user. On opportunities they become pipelines. The connector's standard deal sync creates one per record type, named Salesforce - [record type ID], and the updated deal sync instead maps each record type to a pipeline that already exists. Custom pipelines are capped across all objects at 15 on Starter, 100 on Professional and 350 on Enterprise for seat-based portals. Record types elsewhere, or several sharing one sales process, fit a dropdown property better.
A Salesforce validation rule evaluates a formula across one or more fields and blocks the save when it returns true. HubSpot splits that job three ways, none of which reads a formula. Property validation rules constrain one property's format, range or uniqueness; conditional stage properties require values before a stage; pipeline rules limit the stages a record is created in, skips or leaves backwards. A rule requiring an amount at Closed Won becomes a required stage property, which HubSpot documents as applying when a user creates or moves a record by hand, where the Salesforce rule fired on every save. A comparison between two unrelated fields becomes a workflow that flags the record after saving.
Apex triggers and flows move by no path. They are rebuilt as workflows on Professional or Enterprise, or as custom code actions on Data Hub Professional or Enterprise, and formula fields become calculation or rollup properties. Which automations earn a rebuild is a usage question, answered by the move, rebuild and abandon test in HubSpot migration services.
This configuration waits for the data. HubSpot applies property validation rules to imports, enforces pipeline rules on integration and API writes that pass a user ID, and by default enrolls a record the first time the record meets a workflow's triggers. Rules switched on early reject history, and workflows switched on early treat years of closed deals as events happening today.
Sharing Model Differences
Salesforce sets a baseline per object through organisation-wide defaults of Private, Public Read Only or Public Read/Write, and the role hierarchy and sharing rules can only widen it. HubSpot scopes view and edit permissions to all records, the user's team's records or the user's own, with an optional allowance for unassigned records.
HubSpot visibility therefore derives from ownership and team membership, which makes owner mapping the permission migration. Under the same guide, owner access extends to any user named in a HubSpot user property as well as the record owner, and team scope covers every team a user is assigned to. A record whose Salesforce owner has no HubSpot user arrives unassigned, visible only to users granted all records or the unassigned allowance. An owner-based sharing rule can be approximated through team membership, and a grant to named individuals through a user property. A criteria-based rule opening one class of records to one group has no equivalent, a gap the HubSpot vs Salesforce comparison treats as a selection criterion.
Salesforce to HubSpot Migration Checklist and Load Order
Each step exists because a later one depends on it; the diagram states the constraint behind each position.
- Measure in Salesforce and record the targets: accounts, contacts, unconverted leads, opportunities, opportunities without a contact role, and closed-won amount with and without contact roles.
- Create users under Settings > Users & Teams, recreate teams, and map every Salesforce owner, inactive ones included, to a HubSpot user.
- Under Settings > Properties, create a single-line text property for the Salesforce record ID on contacts, companies and deals, selecting Require unique values for this property on the Rules tab at creation, since uniqueness cannot be added later.
- Create one deal pipeline per opportunity record type in use under Settings > Data Management > Objects > Deals > Pipelines, unless the connector's standard deal sync will create its own, and leave pipeline rules off.
- Load accounts as companies. On the connector path, switch on company sync, then import through CRM > Contacts > Add contacts > Import > Import data > Objects from an app > Salesforce records.
- Load contacts and unconverted leads, and compare the result with the expected count from step 1, not the Salesforce total.
- On the connector path switch on deal sync, then load opportunities as deals, with deal-to-contact associations from Opportunity Contact Roles.
- Load tasks, events and email messages once, after every parent record exists.
- Freeze Salesforce edits, load records whose Last Modified Date follows the extract, switch off the connector sync, and repoint every integration that wrote to Salesforce.
- Switch on validation rules, pipeline rules and rebuilt workflows, one pipeline at a time.
- Verify: the count of deals whose Number of associated contacts is 0 or unknown equals step 1's opportunities without a contact role; closed-won amount per pipeline equals closed-won amount per record type; and one known opportunity traced field by field arrives with its owner and activities.
Salesforce to HubSpot Migration Challenges: Symptoms and Causes
The HubSpot contact count sits far below Salesforce leads plus contacts. The target was wrong, not the load: converted leads duplicate contacts and HubSpot keeps one contact per email, so the fix is the expected count from step 1.
Contacts arrived with an email address and nothing else. The connector imported them outside the inclusion segment, where every property but email stays blank; add them to the segment and resync, or reload properties by import keyed on the Salesforce ID.
Revenue attribution shows a fraction of closed-won revenue. The missing deals have no associated contact because their opportunities had no contact role; the repair is contact roles in Salesforce, or labelled associations in HubSpot.
Imported rows are rejected, closed deals cannot be created, or customers receive emails during the load. Rules and workflows were switched on before step 10: validation rules apply to imports, pipeline rules to integration writes that pass a user ID, and workflows to any record meeting their triggers for the first time. They go off for the load, and workflows return with triggers excluding records that carry a Salesforce ID.
Sales users cannot see records they worked in Salesforce. Owners were never mapped or teams never recreated, so the records sit unassigned or outside the users' team scope.
Deal amounts are wrong in a multi-currency org. The connector does not convert currencies and carries only the number, so amounts need correcting by import.
Salesforce emails are missing from HubSpot timelines. Emails captured by Einstein Activity Capture are not stored as task activities, so the connector never sees them, and they need a separate extract.
Costs and Returns of Leaving Salesforce
Where HubSpot already runs marketing, a migration buys one record per person instead of a lead and a contact, and removes the connector with the reconciliation it maintains. It costs a rebuild: every automation, validation rule and report is re-expressed rather than moved, and custom objects need Enterprise. The firm is a HubSpot Solutions Partner with an interest in migrations happening, which is reason to state plainly where Salesforce is stronger.
Two axes favour Salesforce on the documentation alone. Its sharing model composes defaults, hierarchy and rules into access patterns that owner and team scopes approximate at best, and criteria-based sharing not at all. Its validation rules evaluate formulas across fields on every save, where each HubSpot rule governs one property. The wider platform comparison is the white paper on Salesforce vs. HubSpot CRM in revenue operations.
The case is strongest where the org runs on standard objects, grants access by owner and team, and carries automation that a usage audit shows to be largely dormant. It is weakest where compartmentalised access is a regulatory obligation, where Apex must run synchronously on save, or where reproducing a custom-object model would need Enterprise across the portal. The Salesforce to HubSpot migration case study records one instance, including a capability that did not transfer.
HubSpot to Salesforce Migration: The Reverse Direction
A HubSpot to Salesforce migration runs the same map backwards, and its challenges are the collapses above reversed. Each HubSpot contact becomes a lead or a contact, and the connector's sync setting for that choice warns that leads combined with automatic company creation produce duplicate companies at conversion. Deal-to-contact associations become Opportunity Contact Roles, which the connector creates on sync, and a contact associated with several companies needs one chosen as its account.
Access is where the reverse direction gains: owner and team scopes are redesigned as organisation-wide defaults, roles and sharing rules, which is more work and more expressive. Converted leads and original creation dates can be inserted once Salesforce's Create Audit Fields setting exposes `IsConverted` and the conversion fields to Data Loader.
Scope and Limits of the Evidence
This covers Salesforce sales objects, cases, custom objects and their configuration, not Account Engagement or Marketing Cloud data, Salesforce CPQ, or files at volume. Behaviour, tier gates, limits and navigation paths are as documented in September 2026 by HubSpot and Salesforce, whose documentation governs wherever this text diverges. Salesforce's SOQL reference could not be retrieved for this article, so the coverage query should be tested in a sandbox first.
The coverage ceiling is structural, following from how the standard deal sync and Smart Transfer are documented to build associations. It is untested against the updated deal sync, whose association options were not documented when this was checked. The sample proportions are invented, and no published measurement of contact-role coverage across Salesforce orgs was located, so nothing here says how large the gap is in a typical org.
Frequently Asked Questions
Is there an official Salesforce to HubSpot data migration guide? HubSpot documents two paths rather than one guide, the connector's record import and Smart Transfer, so a Salesforce to HubSpot migration guide built on them still has to supply the load order and expected counts.
What does the Salesforce to HubSpot migration process consist of, and what belongs on its checklist? The eleven steps above. A Salesforce to HubSpot migration checklist that omits the expected contact count and the share of opportunities without a contact role has no targets to reconcile against.
What are the main Salesforce to HubSpot migration challenges? Converted leads, opportunities without contact roles, record types beyond the pipeline cap, cross-field validation rules, Apex and flows, and sharing rules with no HubSpot equivalent.
Can a Salesforce migration to HubSpot run through the connector alone? For standard objects on Professional or Enterprise, and custom objects on Enterprise, it can, although a lookup on a custom object travels only as an association and a change to a formula field does not itself trigger a sync.
What are the HubSpot to Salesforce migration challenges, and can a HubSpot migration to Salesforce use the same connector? The connector syncs both ways, so it can. The challenges are choosing lead or contact for each person, reducing multi-company associations to one account per contact, and designing a sharing model HubSpot never needed.
In Summary
Accounts become companies, unconverted leads and contacts become contacts, opportunities become deals in one pipeline per record type, and Opportunity Contact Roles become deal-to-contact associations. Validation rules, Apex, flows and formula fields are rebuilt, and the sharing model reduces to owners and teams.
Two numbers decide whether the result can be trusted, and Salesforce holds both before anything moves. The expected contact count is unconverted leads and contacts after email deduplication, not their raw total. The share of opportunities with a contact role caps deal-to-contact coverage under the standard deal sync and Smart Transfer, and their closed-won amount caps what revenue attribution can report. Compute both, load in dependency order with every rule off, and reconcile against the computed figures rather than the Salesforce totals.