To migrate agency client data from HubSpot to Salesforce without losing history, export all objects (contacts, companies, deals, activities, notes) with their original timestamps and owner IDs, map fields between the two CRMs, then import in dependency order using a tool like the Salesforce Data Loader or a managed migration service. The key is preserving createdAt dates and engagement records, which HubSpot stores separately from core records.

Why HubSpot-to-Salesforce migrations lose history

Most teams get this wrong because they treat the migration like a simple CSV dump. HubSpot and Salesforce model data differently. HubSpot uses a flat object model with contacts, companies, deals, and tickets, plus a unified engagements API for emails, calls, notes, and meetings. Salesforce splits these into Leads, Contacts, Accounts, Opportunities, and a polymorphic Activity (Task/Event) model.

The history you risk losing falls into three buckets:

  • Timestampscreatedate and lastmodifieddate get reset to import time unless you explicitly map them.
  • Activity/engagement logs — emails, calls, and notes live in HubSpot's engagement objects and are often skipped entirely.
  • Ownership and associations — the link between a deal and its contacts, or a contact and its owner, breaks if IDs aren't mapped first.

If you're still deciding between platforms, compare HubSpot Sales Hub vs Salesforce Sales Cloud before committing to the move.

Diagram comparing HubSpot flat object model with engagements API against Salesforce Lead Contact Account Opportunity Activity data model, arrows showing field mapping

Step-by-step migration that preserves history

1. Audit and clean data in HubSpot first

Deduplicate contacts and companies before you touch Salesforce. Migrating dirty data just moves the mess. Export a full property list from HubSpot Settings > Properties so you know every custom field that needs a Salesforce equivalent.

2. Build a field mapping document

Create a spreadsheet mapping each HubSpot property to a Salesforce field. Flag fields with no destination — you'll either create custom fields in Salesforce or drop them. Pay attention to:

  • HubSpot record ID → store in a custom external ID field on Salesforce (e.g., HubSpot_ID__c). This is critical for re-linking associations.
  • Deal stages → Opportunity stages (the picklist values rarely match).
  • Owner emails → Salesforce User IDs.

3. Create an external ID field in Salesforce

Add a custom field like HubSpot_ID__c marked as External ID and Unique on every object. This lets you upsert records and rebuild relationships without duplicates. It's the single most important step for keeping associations intact.

4. Export from HubSpot in dependency order

Use the HubSpot CRM exports or the HubSpot API for objects with more than 10,000 records. Pull engagement data through the engagements endpoint — the standard UI export won't include call and email logs. Export in this order:

  1. Companies
  2. Contacts
  3. Deals
  4. Engagements (notes, emails, calls, meetings, tasks)

5. Import into Salesforce in the right sequence

Load records so parent objects exist before children. Use the Salesforce Data Loader for bulk operations and upsert keyed on HubSpot_ID__c.

OrderHubSpot objectSalesforce targetOperation
1CompaniesAccountsInsert
2ContactsContactsUpsert
3DealsOpportunitiesUpsert
4EngagementsTasks / EventsInsert

6. Preserve original timestamps

Salesforce normally sets CreatedDate to the import time. To keep the real dates, enable the Create Audit Fields permission (Setup > User Interface > enable "Set Audit Fields upon Record Creation"). Then map HubSpot's createdate to CreatedDate in your import file. Without this, every record looks like it was created on migration day.

Generate Proposals with AI in seconds.

Try now
Proposal album preview

Tools and approaches compared

You've got three realistic paths:

  • Native Salesforce Data Loader + manual mapping — free, full control, but slow and error-prone for engagement history. Best for under 50,000 records.
  • Migration platforms (Trujay, Import2, Coupler.io) — handle field mapping and associations automatically, preserve timestamps, and migrate activities. Costs scale with record volume.
  • Custom ETL via APIs — maximum control for complex agency hierarchies, but requires developer time.

For agencies juggling many client accounts, the association integrity matters more than raw speed. A botched parent-child link between Accounts and Contacts can take longer to fix than the original migration. Teams that have run a Qvidian to Responsive migration know how much cleanup unmapped fields create downstream.

Screenshot-style mockup of Salesforce Data Loader upsert configuration screen with HubSpot ID external field selected as the match key

Validation after migration

Don't trust a green import log. Run these checks:

  1. Record counts — match Salesforce totals against HubSpot exports per object.