Migration Playbook

Salesforce HubSpot

Salesforce to HubSpot: The Complete Migration Playbook

A 37-step runbook across six phases — track your progress, and open the right tool at every step.

0 / 37 steps complete 0%
TL;DR

Migrating Salesforce to HubSpot requires API-based extraction (Bulk API + PK Chunking) and HubSpot batch CRM APIs to preserve Cases, attachments, and associations. CSV exports and the native connector cannot handle relational data or files.

The native HubSpot-Salesforce connector is a sync tool, not a migration engine. It cannot handle historical Cases, file attachments, custom object associations, or retroactive backfill of ticket threads. Salesforce stores files across a three-object model (ContentVersion/ContentDocument/ContentDocumentLink) while HubSpot has no direct attachment object. The fundamental gap is architectural: Salesforce Cases use Status picklists tied to Support Processes with Record Types, while HubSpot Tickets use Pipeline Stages with Open/Closed categories — requiring exact internal stage ID mapping, not label matching. Every migration requires API-based extraction using Bulk API with PK Chunking for large objects, a three-step file workflow (upload to Files API, create Note with hs_attachment_ids, associate with Ticket), and batch CRM object endpoints to bypass HubSpot's 500-import-per-day ceiling.

Read this first

Pair-specific gotchas that catch teams out. Each one has cost somebody a weekend.

The native connector is designed for ongoing bidirectional sync between two live systems

It is not a migration tool. Treating it as one will leave historical data behind, drop custom object relationships, and silently skip file attachments.

Salesforce's daily API limit is a soft limit — the platform may allow temporary overages,

Salesforce's daily API limit is a soft limit — the platform may allow temporary overages, but sustained excess triggers a hard block with HTTP 403 REQUEST_LIMIT_EXCEEDED. Plan your extraction windows to stay well under the ceiling, especially if integrations are still running on the same org.

If your Salesforce org still uses the legacy Attachment object (pre-Lightning), those

If your Salesforce org still uses the legacy Attachment object (pre-Lightning), those records live in a different schema with a ParentId field instead of ContentDocumentLink. Your migration script must handle both models. If you do not audit both, your attachment counts can look right at the Case level but still be wrong at the file level.

Inline image edge case

If historical email HTML references Salesforce-hosted file URLs, moving the binary alone is not enough. The email or note body must be rewritten to point to HubSpot file URLs, or those inline images will still reference Salesforce after cutover.

Case Owner edge case

The Salesforce Case Owner field may not appear in HubSpot's field mapping settings for the native integration. For API-based migrations, map OwnerId to hubspot_owner_id manually, ensuring the user exists in both systems. If Cases are owned by Queues (not individual users), the OwnerId resolves to a Queue record that has no HubSpot equivalent — assign to a default user or map to a HubSpot team.

The 429 Error Trap

Ignoring a 429 error and aggressively retrying bulk imports compounds the issue. HubSpot enforces a burst cap of ~190 requests per 10 seconds. Retrying without pacing adds more calls against the same quota, leading to prolonged lockouts.

Skip the bulk import endpoint for relational data

Use HubSpot's individual CRM object APIs (/crm/v3/objects/tickets, /crm/v3/objects/contacts) with batch endpoints. This bypasses the 500-import-per-day ceiling and gives you full control over associations, timestamps, and error handling per record.

The runbook

Work top to bottom. Tick steps as you go — your progress is saved in this browser.

01 Discovery Agree scope, pipeline design and success criteria before touching data. 0/5

Objective A scope covering every object in the revenue model, with sales leadership signed up to the pipeline design.

  1. Inventory every object in Salesforce

    RevOps 1-2 days

    Count accounts, contacts, leads, deals or opportunities, activities, notes, emails, files, products and quotes. Activity and email history is usually the largest object by an order of magnitude and the one people forget to size.

    Data Profiler Get real record counts instead of estimating from memory
  2. Map the current pipeline and agree the target model

    Sales leadership 3-5 days

    Document every pipeline, stage, probability and required field, then agree the HubSpot model with sales leadership. Migrating a broken pipeline faithfully is a wasted opportunity; redesigning it mid-migration is a wasted migration.

    Stage changes alter historical conversion rates. Agree how you will restate reporting before you change the model.

  3. Catalogue integrations and automation

    RevOps 2-3 days

    List every system touching Salesforce: marketing automation, quoting, billing, ERP, data enrichment, dialler, BI. Each is a separate cutover task with its own owner, and each can silently write bad data into your new CRM.

  4. Capture the reporting that must survive

    RevOps 2 days

    Interview the people who actually live in the dashboards — forecast, pipeline coverage, rep activity, cohort conversion. Every one of these needs the underlying fields to migrate, which frequently expands the scope.

  5. Build the business case and pick the go-live date

    Project sponsor 2 days

    Model licence delta, implementation effort and productivity dip. Then choose the date deliberately: mid-quarter is disruptive, quarter-end is worse. Most teams land on the first week of a new quarter.

    COI & ROI Calculator Build the 36-month business case you will need for sign-off

Don't move on until

  • Object counts confirmed for accounts, contacts, deals and activities
  • Target pipeline and stage model agreed with sales leadership
  • Reporting requirements captured from the people who use the reports
02 Data Audit CRM data is usually dirtier than anyone expects. Find out how dirty. 0/6

Objective A profiled export with duplicates, ownership gaps and relationship integrity all quantified and triaged.

  1. Export and profile every object

    Data engineer 2 days

    Profile null rates, distinct values and type consistency across all objects. Pay attention to fields sales reps were supposed to fill in: an 80% null rate on a field your forecast depends on is a business finding, not a data one.

    Data Profiler Profile the Salesforce export for nulls, outliers and type drift
  2. Quantify duplicates and agree the merge policy

    RevOps 2-4 days

    Measure duplicate accounts (same company, different spellings and suffixes) and contacts (same email, or same person at a renamed company). Agree survivorship rules before merging: which record wins, which fields, and what happens to the activity history on the loser.

    Merging before you have agreed survivorship rules destroys history irreversibly. Decide first, merge second.

    Data Cleaner Strip empty rows, stray whitespace and dead columns
  3. Verify relationship integrity

    Data engineer 1-2 days

    Check every deal has a valid account, every contact a valid account, every activity a valid parent. Referential breaks are the defect that turns a clean-looking load into a CRM where the pipeline report does not tie to the account list.

  4. Resolve ownership and the user map

    RevOps 1-2 days

    Build the Salesforce user → HubSpot user map, including leavers. Every record needs a valid owner: records assigned to deactivated users either fail to load or land unassigned, and unassigned pipeline is pipeline nobody works.

    Records owned by deactivated users are a top cause of load failures. Decide the reassignment target before you start.

  5. Scan for PII and regional compliance

    Compliance / DPO 1-2 days

    CRMs hold marketing consent, lawful basis and unsubscribe state. Confirm these fields migrate intact and that consent provenance survives — losing consent records is a regulatory problem, not a data-quality one.

    PII & Compliance Scanner Find regulated fields before they land in a new system
  6. Clean, normalise and archive the pre-state

    Data engineer 2 days

    Normalise country and state values, phone formats, currencies and casing, and standardise all timestamps to UTC. Keep an untouched copy of the raw export — it is your only reference if a mapping decision turns out wrong.

Salesforce → HubSpot specifics

ZIP files are capped at roughly 512 MB each
Large exports automatically split across multiple archives.
Exports are flattened
Relationships between records — parent-child links, lookup fields, master-detail chains — are not preserved in the output. You get disconnected CSV rows, not a relational dataset. Stitching them back together requires manual VLOOKUP operations.
Frequency is restricted
Enterprise, Performance, and Unlimited editions can export once every 7 days. Professional and Developer editions: once every 29 days.
Files expire in 48 hours
Download links are deleted after 48 hours. Miss the window and you start over.
Association batch reads
are limited to 1,000 inputs per request body. (developers.hubspot.com)

Don't move on until

  • Duplicate rate quantified per object with a merge policy agreed
  • Every record has a valid owner mapped to a target user
  • Relationship integrity verified — no orphaned deals or contacts
03 Field Mapping Map objects, relationships and picklists — in that order. 0/6

Objective A signed mapping spec that covers objects, relationships, picklist values and currency handling.

  1. Map objects and their relationships first

    Solution architect 2-3 days

    Establish how Salesforce objects correspond to HubSpot objects before any field work. Leads-versus-contacts, accounts-versus-organisations and deal hierarchies differ structurally between CRMs, and a field map built on the wrong object model has to be redone.

    Schema Mapper Opens pre-loaded with the Salesforce → HubSpot field pair
  2. Generate and then hand-review the field map

    Solution architect 2-3 days

    Auto-match both schemas, then review every row. Look hardest at anything named "type", "source", "stage" or "status", where names match and meanings do not.

  3. Map every picklist value, including retired ones

    RevOps 2 days

    Enumerate all values on both sides — deal stages, lead sources, industries, statuses — and map each explicitly, including values no longer offered but still present on old records. Unmapped values either fail the row or land as blanks that quietly break segmentation.

    Historical records often carry picklist values that were retired years ago and no longer appear in the UI.

  4. Decide currency, amount and date handling

    Finance / RevOps 1-2 days

    If you sell in multiple currencies, confirm how HubSpot stores amounts, exchange rates and dated conversion rates. Recalculating historical deal values at today's rate silently rewrites your revenue history.

    Multi-currency deals re-converted at current rates will not tie to your historical reporting or your finance system.

  5. Determine the load order

    Data engineer 1 day

    Sequence the load so parents exist before children: users, then accounts, then contacts, then deals, then activities and notes. Keep source IDs in custom fields so relationships can be rebuilt by lookup rather than guesswork.

  6. Freeze the spec and sign off

    Project manager 1 day

    Version-control the mapping, walk RevOps and sales leadership through the decisions that change reporting, and get written sign-off before the pilot.

Don't move on until

  • Object and relationship model mapped and reviewed
  • Every picklist value explicitly mapped, including dead values
  • Load order determined so parents always exist before children
04 Test Migration Pilot with related records, not a flat sample. 0/7

Objective A pilot load whose relationships, ownership and roll-up reporting all verify against source.

  1. Configure the HubSpot sandbox to match the agreed model

    Solution architect 3-5 days

    Build the pipelines, stages, custom fields, record types and user roles first. Load into a default configuration and you will only discover the configuration defects at full scale.

  2. Select a connected pilot slice

    Data engineer 0.5 day

    Take complete account trees — 50-100 accounts with all their contacts, deals, activities and files — rather than a flat random sample. Relationship defects are the whole point of a CRM pilot and a flat sample cannot expose them.

  3. Run the load in dependency order with full logging

    Data engineer 1-2 days

    Load users, accounts, contacts, deals, then activities, logging every request against its source ID. Note which failures are transient (rate limits, timeouts) and which are structural (validation, missing parent).

  4. Verify relationships and roll-ups

    RevOps 1-2 days

    Confirm every deal sits on the right account with the right owner and stage, and that pipeline totals per account and per rep match Salesforce exactly. Roll-up mismatches almost always mean a relationship or currency defect upstream.

    Migration Validation Tool Diff the pilot batch against source before scaling up
  5. Measure throughput and project the full load

    Data engineer 1 day

    Record actual records-per-hour under HubSpot's API limits and extrapolate, remembering activity history usually dominates volume. If the projection exceeds your window, split the load or trim activity scope now.

  6. Let reps work the pilot data

    Sales leadership 2-3 days

    Put two or three reps in the sandbox against their own accounts. They spot missing context, wrong owners and unusable notes far faster than any reconciliation script, and their buy-in is what makes adoption work.

  7. Validate in a sandbox

    Run your full migration against a HubSpot sandbox portal before touching production. Test field mappings, associations, file uploads, and timeline rendering in the sandbox. Fix mapping errors and edge cases here — not during your production cutover window.

Don't move on until

  • Relationships intact across the whole pilot slice
  • Pipeline roll-ups match source for the pilot accounts
  • Reps have worked pilot records and confirmed they are usable
05 Cutover Switch the revenue system with the pipeline intact. 0/7

Objective All in-scope CRM data live in HubSpot, integrations repointed, and reps selling on day one.

  1. Pre-load history ahead of the freeze

    Data engineer 5-10 days

    Load closed deals, historical activities and inactive accounts while Salesforce stays live. Only open pipeline and the final delta need to move inside the window.

  2. Publish the cutover runbook

    Project manager 1 day

    A timed sequence with owners and abort criteria: freeze, final export, delta load, integration switch, smoke test, go/no-go, rep enablement. Pick a window that avoids quarter-end and month-end close.

  3. Freeze Salesforce and take the final delta

    RevOps 2-4 hours

    Set Salesforce read-only and export everything changed since the pre-load. Reps updating deals during an unenforced freeze is the classic way to lose the most recent — and most valuable — pipeline changes.

    A rep who updates a deal in the old CRM during the freeze will lose that update permanently.

  4. Load the delta and reconcile the pipeline

    RevOps 2-6 hours

    Run the delta, then verify open pipeline value and count per rep against Salesforce before anything else. If the pipeline number is wrong on day one, reps stop trusting the system and never fully come back.

    Migration Validation Tool Confirm the final delta landed before you reopen
  5. Repoint every integration and verify with real records

    IT / integrations 4-8 hours

    Switch marketing automation, quoting, billing, enrichment, dialler and BI connections, then push a live record through each path. An unrepointed integration writing into the old CRM causes divergence that gets harder to reconcile every day.

    Two-way integrations left pointing at the old CRM will keep writing there. Disable them before you switch, not after.

    Cron Expression Builder Schedule the delta syncs that run through the freeze
  6. Go/no-go, then enable the reps

    Project sponsor 1-2 days

    Call the decision explicitly against the exit criteria. Run enablement on real data with their own accounts on screen, and keep Salesforce read-only rather than cancelled until validation closes.

  7. Run a delta pass

    Re-extract records changed after the historical snapshot and update only those rows. This keeps both systems current during the validation period.

Salesforce → HubSpot specifics

Model the target first
Create pipelines, stages, custom properties, unique source-ID properties, and owner crosswalks in HubSpot before loading any data.
Backfill history in layers
Load base objects first (Contacts, Companies), then Tickets with associations, then activities (Notes, Emails), then file attachments. Dependency order matters — you cannot associate a Note with a Ticket that does not exist yet.
QA against business questions, not just counts
Reopen ten old cases. Check whether attachments load, timestamps sort correctly, owners look right, and company/contact associations match what agents expect. A matching record count is not proof of a good migration — if a reopened ticket is missing the screenshot or the original email thread, agents still lost the context they need.
Cut over with a small freeze window
Freeze admin changes and routing logic, not the whole support team. Validate live writes immediately after switchover. Agents should log into HubSpot with every pipeline stage, ticket thread, and attachment exactly where it belongs.

Don't move on until

  • Historical load complete and reconciled before the freeze
  • All integrations repointed and verified end to end
  • Reps logged in with pipeline visible and correct
06 Validation Prove the revenue numbers tie out, then close the project. 0/6

Objective Reconciled data, forecast parity with pre-migration reporting, and signed acceptance.

  1. Reconcile every object

    Data engineer 2 days

    Compare counts and values across accounts, contacts, deals, activities and files, plus field-level spot checks on a random sample. Produce a single report suitable for audit and for finance.

    Migration Validation Tool Reconcile Salesforce and HubSpot record-for-record
  2. Tie the pipeline and forecast to baseline

    RevOps 2-3 days

    Rebuild forecast, pipeline coverage and win-rate reporting and compare to pre-migration figures. Every variance needs an explanation — most trace back to stage or currency mapping decisions made in Field Mapping.

    Stage-model changes will legitimately shift historical conversion rates. Restate the baseline rather than chasing the difference as a defect.

  3. Verify ownership, visibility and permissions

    RevOps 1-2 days

    Confirm every record has the right owner and that sharing rules, territories and role hierarchy give each rep exactly the visibility intended. Over-broad visibility in a CRM is a data-protection issue as much as a config one.

  4. Re-profile for field completeness

    Data engineer 1 day

    Compare loaded null rates against the source profile per field. A field that arrived empty while counts matched is the failure counts alone cannot detect.

    Data Profiler Prove field completeness held up through the load
  5. Test automation and integration write-back

    RevOps 2 days

    Fire every workflow, assignment rule, sequence and notification, and confirm each integration writes correctly in both directions. Automation is rebuilt rather than migrated, so it is unproven until observed.

  6. Sign off and schedule decommission

    Project sponsor 1 day

    Get written acceptance against the Discovery criteria, keep Salesforce read-only for 30-90 days with a final archive export taken, then diarise cancellation so the contract does not auto-renew.

Don't move on until

  • Full reconciliation report complete across all objects
  • Forecast and pipeline reports tie to pre-migration baselines
  • Acceptance signed and Salesforce decommission scheduled

Field mapping reference

The field-by-field mapping for each object. Use this as the starting point for your mapping spec.

Case Ticket Property 11 fields medium

Core field mapping is direct but pipeline stage IDs must match exactly; Record Types have no HubSpot equivalent

Salesforce fieldHubSpot fieldNotes
Subject subject (hs_ticket_name) Direct map
Description content (hs_ticket_description) Direct map
Status hs_pipeline_stage Requires pipeline stage mapping
Priority hs_ticket_priority Values may differ (e.g., "Medium" vs "MEDIUM")
Origin Custom property No native equivalent — create a custom property
CaseNumber Custom property Preserve for cross-reference during QA
CreatedDate hs_createdate Use hs_timestamp for historical imports
ClosedDate Custom property No direct "closed date" in HubSpot — use custom property
ContactId Association to Contact Must resolve Salesforce Contact ID → HubSpot Contact ID
AccountId Association to Company Must resolve Salesforce Account ID → HubSpot Company ID
OwnerId hubspot_owner_id Requires user mapping; Salesforce Queue owners have no HubSpot equivalent

Risk matrix

Per-object risk for this pair. Plan extra validation around anything marked high.

ObjectRiskNotes
Cases → Tickets medium Core field mapping is direct but pipeline stage IDs must match exactly; Record Types have no HubSpot equivalent
CaseComments → Notes medium Requires individual API calls per comment with hs_timestamp for original date preservation
EmailMessages → Email Engagements high Native threading is lost; existing emails cannot be updated post-import; first write must be correct
ContentVersion/Files → Files + Notes high Three-API-call process per file with no batch support; inline images require URL rewriting
Custom Objects high Native connector limited to 10 custom objects; Enterprise-only sync; associations don't carry over
Pipeline Stages/Status medium Status picklist to pipeline stage ID mapping must be exact; HubSpot rejects unknown values
Record Types medium No HubSpot equivalent; must map to separate Pipelines or flatten into a custom property
Person Accounts high No HubSpot equivalent; must split into separate Contact and Company records with associations
Picklist Values medium Format differences (spaces, case, special characters) cause silent import failures
Case Owner/Queues medium Queue owners have no HubSpot equivalent; require default user assignment or team mapping

The hard parts

What makes this specific migration difficult, beyond the mechanics.

File Attachment Workflow

Each file requires three sequential API calls — upload to HubSpot Files API, create Note with hs_attachment_ids, then associate with the target Ticket. No batch upload support exists.

Pipeline Stage Mapping

Salesforce Case Status tied to Support Process must map to HubSpot pipeline stage internal IDs. Mismatches cause import failures — HubSpot rejects tickets with non-existent stage values.

Import Limit Ceiling

HubSpot's hard 500-imports-per-24-hour limit blocks chunked CSV approaches. Batch CRM object APIs must be used instead for relational data.

Timestamp Preservation

Without explicit hs_timestamp on every engagement, HubSpot defaults to import date — destroying historical reporting and ticket timeline chronology.

PK Chunking Requirement

Large objects like EmailMessage and ContentVersion with 10M+ records require PK Chunking to prevent QUERY_TIMEOUT errors during Salesforce extraction.

Tools used in this playbook

All free, all run entirely in your browser — nothing is uploaded.

FAQ

Can the native HubSpot-Salesforce integration migrate historical Cases and attachments?

No. The native connector only syncs records when they are created or updated going forward. Historical Cases must be imported separately via CSV or API-based migration. The connector also cannot migrate file attachments or custom object associations.

How do you migrate Salesforce file attachments to HubSpot?

Salesforce stores files as ContentVersion/ContentDocument objects with binary data linked via ContentDocumentLink. You must extract files via the Salesforce REST API, upload each file to HubSpot's Files API, then create a Note with hs_attachment_ids and associate it with the target Ticket. This is a three-API-call process per file — CSV exports cannot handle binary data.

What is the HubSpot import limit per day?

HubSpot enforces a hard limit of 500 imports per rolling 24-hour window per portal, regardless of subscription level. Exceeding this triggers a 429 error (PORTAL_DAILY_IMPORT_EXCEEDED). For large migrations, use HubSpot's batch CRM object APIs instead — they bypass the import limit and accept up to 100 records per API call.

How do you map Salesforce Case Status to HubSpot Ticket Pipeline?

Salesforce Cases use a Status picklist tied to a Support Process. HubSpot Tickets use Pipeline Stages with Open/Closed categories. Create matching pipeline stages in HubSpot before migration and build a mapping table translating each Salesforce Status value to a specific HubSpot stage ID. The Tickets API requires the internal stage ID, not the label. Mismatches cause import failures.

Why does PK Chunking matter for Salesforce data extraction?

PK Chunking splits Bulk API queries into batches based on record IDs, preventing query timeouts on large objects. The default chunk size is 100,000 records (max 250,000). Without it, queries against objects like EmailMessage or ContentVersion with 10M+ records will time out. Bulk API 2.0 performs PK chunking automatically; Bulk API 1.0 requires the Sforce-Enable-PKChunking header.

Or skip all of this and let us handle it

Book a 30-minute call and we'll scope your migration in a single session.