RevOps HQ
← BACK TO BLOG
8/6/2026
CRM Migration

CRM Migration - Planning, Mapping, Execution and Reconciliation

A complete migration process for moving into HubSpot - scoping what moves, auditing the source, field mapping, the import identifier that decides create versus update, cutover, and the reconciliation that proves it worked.

P

Paul Maxwell

AUTHOR

GET WEEKLY REVOPS INSIGHTS

No spam. Unsubscribe anytime.

A CRM migration moves records, associations and history from one system into another and ends with both sides reconciled. It is not an import. An import is one step inside it, and treating the two as the same thing is why migrations produce duplicate records, orphaned associations and a sales team that keeps the old system open.

This is the full process in execution order, the mechanics that decide whether records update or duplicate, and the reconciliation that tells you it is finished.

Terms

Record ID — HubSpot's system-generated identifier for every record. The only reliable key for updating an existing record.

Unique identifier — the column an import matches on to decide whether to update an existing record or create a new one.

Association — the link between two records. Associations migrate separately from the records themselves and are where most migrations lose fidelity.

Cutover — the point at which the old system stops being authoritative.

Reconciliation — comparing counts and totals on both sides afterwards to prove nothing was lost.

Three things migration is routinely mistaken for

A migration is not a data transfer, because a transfer that faithfully reproduces the source system's structure also reproduces every accumulated problem in it, and the move is the only inexpensive opportunity you will get to change that structure before the data lands.

Neither is it a cleanup, although it forces one. The cleaning has to happen in the source before anything moves, because cleaning in transit destroys your ability to reconcile the two sides afterwards — you cannot prove a record is missing when you deliberately altered thousands of them on the way through.

It is also not finished at import, which is where most project plans stop. It is finished when both sides reconcile against agreed numbers and the team has stopped opening the old system.

Deciding what moves

Scope should be decided by what the business needs in order to operate, rather than by what happens to exist in the source.

The uncontroversial part moves without discussion: open and recently closed deals, contacts and companies with activity inside a defined window, and anything referenced by an open deal regardless of its age.

What is genuinely arguable is closed-lost deals older than a few years, contacts with no activity since the previous system was implemented, and full activity history — each of which carries a real cost in volume, load time and reconciliation effort.

Activity history is the expensive one, because it multiplies record volume by an order of magnitude, slows every load, and is consulted far less after go-live than it is defended during scoping. A defensible compromise is to migrate activity on open deals and recently active accounts, and archive the rest as an export that can be searched but not queried in the CRM.

Whatever is excluded should be written down and signed off, because the question "where is the 2019 data" arrives eventually and the answer needs to be a decision rather than a discovery.

Auditing the source

Audit before quoting, designing or promising a timeline, because in the migrations we have run the overruns traced back to source data being materially worse than it was described as being at scoping.

Count records per object, and count how many of those have the fields the target model requires populated. A company object with 40,000 records and 6,000 populated industry values is a different migration from one with 40,000 populated.

Then measure duplicates, orphans and referential integrity — records associated to something that no longer exists. Check the picklist values that are actually in use rather than the ones configured, because free-text fields accumulate variants that will each become a distinct option on import.

The audit output is a set of numbers you will reconcile against later, so record them formally rather than reading them once.

Designing the target model

Design the HubSpot model from the business process and then map the source into it, because doing the reverse and recreating source fields one for one imports a decade of accumulated decisions that nobody now remembers making or can justify.

The constraint that matters during design is that HubSpot property internal names are permanent and field types are effectively permanent. A property created to hold a source system's value carries that name forever, so decide whether each field is genuinely needed before creating it.

Create a dedicated property to hold the source system's record identifier, as a text property with unique values enabled, since this is what makes reconciliation possible and re-runs safe. Uniqueness has to be set when the property is created because it cannot be added afterwards, a constraint covered in HubSpot's guidance on creating and editing properties.

Field mapping

The mapping document is the migration's specification, and every source field needs a recorded decision within it — mapped to an existing property, mapped to a new one, transformed on the way, or deliberately dropped.

Transformations need writing down precisely, covering the source values, the target values and the handling of anything unmatched, because an instruction like "map status to lifecycle stage" leaves every actual value to the judgement of whoever builds the file.

Unmatched values are where mappings fail quietly, so decide in advance whether an unrecognised value should fail the row, land in a documented default, or be written to a holding property for review — since declining to decide simply hands the choice to the import tool.

Fields that map to nothing should be listed as dropped rather than omitted, so the decision is visible when someone asks later.

Clean before you move

Deduplication, referential integrity and picklist normalisation all belong in the source system rather than in the migration itself.

The reason is reconciliation: if the source holds 42,310 companies and HubSpot ends with 41,890 because duplicates were merged in transit, you have no way to distinguish deliberate merges from records that were simply lost, short of re-deriving the entire operation.

Cleaning first and re-counting afterwards gives you a set of numbers the migration can be measured against.

The mechanic that decides create versus update

This single behaviour governs whether re-running a migration repairs it or doubles it, which makes it worth settling before the first file is built.

HubSpot deduplicates contacts on email address and companies on domain by default, as described in HubSpot's documentation on contact deduplication. Where a file includes a Record ID column, rows carrying a value update that record and rows without one create new records.

A custom property with unique values enabled can also serve as the import identifier, which is supported across contacts, companies, deals, tickets and custom objects. This is why the source system's identifier should be stored in such a property from the first import: it makes every subsequent run an update rather than a duplication.

The failure mode is specific. A first import creates records. Someone finds an error and re-imports a corrected file without an identifier column. Contacts deduplicate on email and survive; companies without a domain value do not, and deals never deduplicate automatically at all. The result is a portal with clean contacts and duplicated deals, discovered when pipeline reporting doubles.

Import mechanics worth knowing before you build the files

HubSpot accepts .csv, .xlsx and .xls, with a maximum file size of 512MB on paid accounts and 20MB on free ones. A file may contain up to 250,000 records and fewer than 1,000 columns, must have a single sheet, and must carry a header row where each column corresponds to a property. These are set out in HubSpot's guide to setting up import files.

Associations can be created within a single file by putting both records' data on the same row, or across two files by including a common column that appears in both. Deciding which approach applies to each object pair before exporting saves rebuilding the files.

Import order follows dependency, which in practice means companies before contacts, both before deals, and deals before anything associated to them. An association cannot be created to a record that does not yet exist, and the error this produces is easy to overlook inside a large import's summary.

Test migration

Run the whole process against a representative subset before running it at volume, where representative means it deliberately includes the awkward cases — records with missing required fields, non-ASCII characters, values at the maximum length, records carrying many associations, and records that already exist in the target.

Then inspect the records themselves rather than the success summary, confirming that associations formed, that dates carry the intended timezone, that picklist values matched existing options instead of creating near-duplicates, and that no field truncated silently.

A test migration whose only evidence is a green summary has demonstrated that the import ran, not that it was correct.

Cutover

Cutover needs a freeze window in which the source is read-only, agreed with the people who will be told to stop working.

The sequence: freeze the source, export the final delta, import it, reconcile, then switch access. Reconciliation happens before access switches, not after — the freeze window exists so you have the option to stop.

Rollback in practice means continuing on the old system, so it should stay available and unchanged until reconciliation passes. Deleting or disabling it on cutover day removes the only recovery path that actually exists.

Reconciliation

Reconciliation is the proof, and it is the step most often replaced by a feeling that it went fine.

Compare record counts per object against the cleaned source counts, then association counts, which is where losses concentrate because associations are created in a separate pass from the records they join. Financial totals — open pipeline value and closed-won by period — are worth comparing last, since a total matching to the currency unit is strong evidence that deals migrated with their values intact.

Then sample. Pick records across the range, including the awkward ones from the test, and compare them field by field against the source.

Every discrepancy needs an explanation rather than a tolerance, because a difference of eleven records is a difference of eleven records, and "probably duplicates" describes a guess rather than a cause.

After cutover

The first month determines whether the migration holds, and three signals show it earlier than any survey will.

Whether the old system is still being opened is the honest adoption metric, since people revert to what they trust. Whether records are being created rather than only read distinguishes real use from passive reference. And whether the fields the team was asked to populate are actually being populated indicates whether the process was adopted or merely announced.

Expect a queue of small data corrections in the first fortnight and staff someone to work it. The alternative is that people conclude the data is wrong and revert to their own spreadsheets, which is difficult to reverse once it starts.

Cost and duration

Duration is driven by source data quality, object and field count, whether activity history moves, integration count, and how quickly definitional questions get answered.

In our engagements a single-object migration from a clean source has run one to two weeks, while a full multi-object migration carrying activity history from a source needing real cleanup has run six to twelve. Treat those as our experience rather than a benchmark.

The audit result moves the estimate more than any other input, which is the argument for auditing before quoting rather than after. Two clean systems migrate quickly. Two systems with years of duplicates take longer to reconcile than to move — and migrating first simply reproduces the duplicates at the far end.

What this does not cover

Source-specific mechanics — Salesforce record types, Pipedrive pipeline structures, Dynamics option sets — each need their own treatment.

Migrating between two HubSpot portals behaves differently from migrating from a third-party CRM and is not described here.

The figures above are HubSpot's documented limits at the time of writing. Verify them against current documentation before building files at the boundary.

Verification checklist

  • Source audit numbers recorded formally before any design work
  • Exclusions written down and signed off
  • Target model designed from process, not copied from the source
  • A unique-value property holds the source identifier, created with uniqueness enabled
  • Mapping document specifies every field, including dropped ones and unmatched-value handling
  • Cleaning completed in the source and counts re-taken afterwards
  • Test migration checked record by record, including awkward cases
  • Import order respects dependencies
  • Freeze window agreed with the people it affects
  • Reconciliation passed before access switched
  • Every discrepancy explained rather than tolerated
  • Old system still available and unchanged
  • Someone assigned to the first fortnight of corrections

Migration scope and pricing can be configured on our store.

Our HubSpot Services

From implementation to optimization, we handle every aspect of your HubSpot journey

WEEKLY PROGRAM

RevOps Office Hours

A recurring weekly RevOps operating program. Live support plus hands-on HubSpot implementation work.

$1,500/mo
Monthly Operating Program
  • 1 live Office Hours session per week
  • 4 hours of hands-on implementation work per month
  • We determine how hours are allocated based on priorities
  • Recurring monthly cadence