Every CRM data migration checklist you will find says the same eight things: audit, back up, map, dedupe, test, import, validate, train. They are all correct and none of them are the reason migrations go wrong.
Here is the honest version. The import will tell you it succeeded. It will be lying by omission, because the things that fail during a CRM migration fail silently: unmapped fields are discarded without warning, invoice line items collapse to a single line, note timestamps get rewritten to today's date, and job photos never move at all. You find out in week three, when a tech opens a customer record before a call and the history is empty.
This checklist is written for service businesses, where the data is shaped differently than the Salesforce-to-HubSpot migrations most guides describe. Your atomic unit is not a company. It is a property, with equipment on it, with a job history attached. That distinction breaks almost every generic checklist on the first step.
The short answer
A CRM data migration runs in eight steps, in this order:
| # | Step | What it actually decides |
|---|---|---|
| 1 | Inventory the objects you own | Whether you discover the losses now or in month two |
| 2 | Export completely, not conveniently | Whether line items and expenses come with you |
| 3 | Dedupe at source | Whether your team trusts the numbers in week one |
| 4 | Map every field explicitly | Whether anything is discarded without telling you |
| 5 | Decide what you are not migrating | Whether you move the mess into a nicer building |
| 6 | Load in dependency order | Whether relationships survive the trip |
| 7 | Test on twenty records, reconcile | Whether you catch a broken mapping at 20 records or 20,000 |
| 8 | Cut over, validate, keep the old system read-only | Whether you can answer "where did that field go" in March |
Budget two to four weeks of calendar time and 20 to 40 hours of real work for a few thousand customers. The export is hours. Everything else is the project.
Why generic checklists fail service businesses
Generic CRM migration guides model your business as companies, contacts, deals and activities. That model was built for B2B software sales, and it has no place to put the three things a contractor actually runs on.
The property, not the company. A homeowner with a rental duplex is one customer and three service addresses. Housecall Pro's import field list treats service address and billing address as genuinely separate fields with separate notes, which is correct. But ServiceTitan's guide to exporting Housecall Pro data warns about the consequence: "If you have multiple addresses on file for some of your customers, your exported customers report will include additional sets of address columns." Your CSV has a variable number of columns depending on which customer has the most properties. Any importer expecting a fixed column order chokes, and any spreadsheet cleanup that assumes a stable layout quietly shifts data into the wrong fields.
The equipment, not the deal. The serial number, model, install date and warranty expiry on the furnace in that basement is the single most valuable record a service company owns. It is what lets you avoid billing for warranty-covered work, and it is what tells you which customer to call in 2027. It is also the object no generic checklist mentions. Note that Housecall Pro's own documentation scopes equipment migration to their MAX plan, handled by their Data Import team alongside customers, job history and price book. On a lower tier, equipment is your problem.
The attachment, not the note. Job photos, signed estimates, permits and load calculations. These are frequently the largest data volume in the account and the least likely to move.
Watch out
Before you sign anything, ask the new vendor one question in writing: "Which of my objects do you import, and what happens to a field I cannot map?" The answer separates vendors who have done this from vendors who will hand you a CSV wizard and wish you luck.
The nine things that vanish silently
This is the part no competing checklist publishes, because it requires reading the vendors' own import documentation rather than describing migration in the abstract. Each of these is documented behaviour, not speculation, and none of them produces an error.
| # | What disappears | Why | The fix |
|---|---|---|---|
| 1 | Any field you did not map | Housecall Pro: "fields that are not mapped will be discarded" | Review every mapping screen before continuing, hover each column to preview |
| 2 | Invoice line items past the first | Importers expect one row per invoice; exports give one row per line item | Merge rows into a single row per invoice on the invoice number before upload |
| 3 | Custom fields with no destination | HCP's guide states custom fields cannot be created | Concatenate into the notes field with a label prefix, e.g. "pH Level: 7.2" |
| 4 | Sub-customers and parent-child billing | HCP supports "only one level of customers"; child accounts are not imported | Flatten sub-customers onto their own rows before import, rebuild the hierarchy after |
| 5 | Note and activity timestamps | Many platforms stamp imported records with the import date | Prefix each note body with its real date and source so the timeline survives in text |
| 6 | Job photos and attachments | Usually not included in any standard export | Bulk download separately, keep the old account read-only, re-attach only what is active |
| 7 | Material and expense records logged to jobs | Live in reporting exports, not the standard data export | Pull a custom expense report filtered by date range |
| 8 | Recurring job templates and maintenance agreements | Commonly not exportable at all | Screenshot them and rebuild by hand. This is your recurring revenue |
| 9 | Everything past row 1,500 | Some exports cap rows per file and split into multiple emailed files | Count your records first, then count the rows you received |
Number 5 deserves a specific mention because it is the most quietly destructive. A practitioner posting a PSA in r/gohighlevel documented that GoHighLevel "stamps every imported note with the import date, not the original date. So years of history all collapse to 'today.'" Their conclusion is worth quoting directly: "For a CRM that kills the whole point of notes, the timeline's gone." Their workaround, after finding no native fix, was to write the original date and source into the body of every note before importing. It is inelegant and it works.
By the numbers
Industry estimates put typical duplicate rates at 10 to 30% of CRM records, and pre-migration cleaning is widely cited as 3 to 10 times cheaper than fixing the same records after they land. A contractor who documented moving nine years off Jobber found 34 duplicate pairs in 847 clients, plus about 180 clients with no email address at all.
Step 1: Inventory the objects, not the records
Write down every object you own and, next to each, the count. Not "we have about 900 customers." A real number per object:
- Customers, and separately, service addresses (these are different numbers)
- Equipment records, with how many have a serial number and install date
- Jobs and work orders, open versus closed
- Estimates, open versus expired
- Invoices, and separately, invoice line items
- Payments and credits
- Attachments, with total file size
- Price book services and materials
- Recurring templates and maintenance agreements
- Team members and their roles
- Tags, lead sources, job types and business units
The gap between customers and service addresses is your first finding. The gap between invoices and invoice line items is your second, and it tells you exactly how bad the row-collapse problem will be. If you have 4,000 invoices and 14,000 line items, a naive import loses 10,000 rows of detail and every invoice total will be wrong.
Step 2: Export completely, not conveniently
Neither major field service platform gives you one complete file, and both have a default that quietly under-exports.
Jobber. Exports live in Settings under Data Export, and clients, job history and invoice history come out as separate CSVs. Per a contractor's firsthand account of the process, published as a guest post on the Bit and Grain blog, the client list exports cleanly with name, address, email and phone. The job export includes title, client, status, created date and scheduled date, but "does NOT include the line item detail from invoices, the material costs logged to the job, or the notes you have added along the way." Invoice line items require a separate pull from Reports. Material and expense records need a custom expense report filtered by date range. And in his words: "Job photos and attachments do not export automatically. Recurring job templates do not export."
Housecall Pro. Only employees listed as Admins can export customers and jobs. Both arrive by email as CSVs from their notifications address rather than downloading in-browser, so budget an hour of waiting. The critical default: ServiceTitan's migration documentation instructs you to set the date range back to "when you first started using Housecall Pro" and, in Table Columns, "select all items." Skip that and you export the columns currently visible on your screen, which is a fraction of what exists. The same guide notes that if the report errors, you should run it in one-year batches.
Reconcile every file against your Step 1 counts before you go further. If you counted 2,140 customers and the CSV has 1,500 rows, you are holding one of several files.
Step 3: Dedupe at source, before mapping
Duplicates are not a cleanup task for later. They propagate. One duplicate customer becomes two service addresses, two job histories, two invoice streams and two sets of equipment, and separating them after import means merging records that now have real activity attached to both.
A migration consultant writing in r/CRM put the human consequence sharply: if you do not resolve identity before import, "you ship duplicates on day one, reps stop believing the numbers in week one, and by month two they're keeping their real pipeline in a spreadsheet again. Now you have two CRMs and paid for the privilege."
For service businesses, match on the service address plus phone, not on the customer name. Names are unreliable: the same household appears as "Bob Smith," "Robert Smith," "Smith, Robert" and "Bob and Sue Smith" over ten years. The property address barely changes. Decide survivorship rules in advance, meaning which record's phone number wins when two disagree, and apply them consistently.
We run this object inventory and export audit as the first hour of every scoping call, including for businesses moving to a platform we did not build. If Jobber or Housecall Pro fits how you work, we will tell you to buy it. The builds worth doing are the ones where no packaged tool can model the properties, equipment and recurring work you actually run.
Step 4: Map every field explicitly, and kill the orphans
Every field gets one of four decisions written next to it: migrate, transform, archive, or drop. "Keep everything" is not a decision, it is a deferral, and the importer will make the decision for you by discarding it.
Work through the mapping screen slowly. Housecall Pro's wizard lets you hover each field to preview the column contents, which is the only chance you get to catch that your "Organization" column landed in the wrong destination. Where the destination has no matching field and custom fields cannot be created, use their documented workaround: prefix the value with a label and merge it into notes, so "7.2" becomes "pH Level: 7.2" and stays legible to a human later.
Three transformations to run before upload, all of which fail silently otherwise:
- Dates to ISO format. Convert every date column to YYYY-MM-DD as text. Day-first and month-first are ambiguous, and 03/04/2026 flips to a different date without erroring. This trap is covered in more depth in our spreadsheet to CRM migration guide, and it applies identically here.
- One row per invoice. Merge multi-line invoice rows on the invoice number, combining line items into a single field. Housecall Pro's formatting guide is explicit that multiple rows for the same job "will not upload successfully," and that uploading only the first occurrence means "additional data existing in subsequent rows (such as the additional line items) will not import."
- One sheet, not many. Most importers accept a single sheet. If your job detail and invoice header are separate reports, join them on the invoice number yourself before uploading.
Step 5: Decide what you are not migrating, the contractor way
This is where standard CRM advice is actively wrong for a service business, and it is the highest-value contrarian call on this list.
Generic guidance says archive any contact untouched in 24 months, because B2B contact data decays and old records are mostly fiction. For a software sales pipeline, that is right. The contact left the company, so the record is dead.
For a contractor, the homeowner did not leave the house. A customer you have not spoken to since 2011 is not a dead lead, they are a replacement job approaching. Furnaces average roughly 15 to 20 years and air conditioners roughly 10 to 15, per manufacturer lifespan guidance from Carrier and Trane, so the 2011 install in your "dormant" pile is the 2026 quote you are about to lose to whoever knocks first.
So invert the rule. Segment by equipment age and install date, not by last contact date.
- Migrate in full: every customer with equipment on site, regardless of when you last spoke to them. This is your replacement pipeline and it is the most valuable list you own.
- Migrate in full: anyone under a maintenance agreement, plus open jobs, open estimates and unpaid invoices.
- Migrate as a summary: closed job history older than about three years. One line per job, with date, description and amount, is usually enough to answer "have we been here before and what did we charge."
- Leave behind: one-time calls with no equipment installed and no contact detail, duplicate shells, and anything with neither a valid phone nor a valid address.
The same r/CRM consultant framed the general principle well: "History is a trap that looks like a feature. Everyone wants 'all the notes.' Almost nobody needs all the notes." That holds. The contractor amendment is that equipment records are the exception, and they are the one thing you should bring even when the contact looks cold.
The contractor who documented his Jobber move made exactly this call by feel: of 847 clients, about 320 had a job or estimate within two years. He imported all 847 anyway, and re-created his 27 active jobs by hand rather than importing them, on the grounds that active jobs carry context, what is done, what is left, what materials are on site, that "would not survive a CSV import cleanly."
Step 6: Load in dependency order
Records reference each other by ID, and those IDs change when they land in the new system. Load in the order that lets each step capture the IDs the next step needs:
Team members → customers → service addresses → equipment → price book → jobs and work orders → invoices and line items → payments → attachments → notes and activities.
Attachments go near the end deliberately. They are usually the largest volume, they are the most likely to hit an upload size limit, and a file that fails is far easier to retry when every record it attaches to already exists. Import in batches with validation between them, not one big upload. As the GoHighLevel PSA put it: "Import in batches. Test batch, validate, spot check the weird ones, then move the next. Never big-bang it."
Match on email and phone before creating anything, so that if you have to re-run a batch you update records instead of duplicating them.
Step 7: Test on twenty records, then reconcile field by field
Import twenty records. Not a sandbox, and not twenty easy ones. Choose deliberately:
- The customer with three service addresses
- The largest multi-line invoice you have
- Your oldest record, from whenever the business started
- A name with an apostrophe or hyphen (O'Brien, Smith-Jones)
- A record where the billing address differs from the service address
- A customer with equipment installed but no job in five years
- A job with photos attached
Then open both systems side by side and compare field by field. Not "did it import." Did the third property arrive, did all four invoice lines arrive, did the total match, did the apostrophe survive the encoding, is the note dated 2019 or today.
A test import catches a broken field mapping while it costs you twenty records to fix instead of twenty thousand.
Tip
Do not delete and re-import if something lands wrong. Housecall Pro's documentation explicitly advises against it, warning it "can cause further complications," and says their support team can reverse the initial import instead. Most platforms have a similar reversal path. Ask before you start what it is.
Step 8: Cut over, validate, and freeze the old system
Pick a hard cutover date at a period end, in your shoulder season. For HVAC that means spring or autumn, never July or January. The go-live fortnight consumes your office staff, and it should land when dispatch is slowest.
On the day, make the old system read-only rather than cancelling it. Keep the subscription for a few months if attachments and old history still live there, which they probably do. Running both as live systems is the failure mode: two sources of truth means neither is complete and nobody trusts either. Read-only is not parallel running, it is an archive you can still query.
Then validate, on every object rather than just contacts:
- Records in versus records out, per object, as a count
- Sum of invoice totals in the new system versus the old, to the dollar
- Count of service addresses, not just customers
- Count of equipment records with a populated serial and install date
- Spot check twenty records field by field, using the weird ones from Step 7
- Confirm every open job, open estimate and unpaid invoice is present
If the invoice totals do not reconcile to the dollar, you have found the line item collapse. Fix it before go-live, not after.
The two weeks after go-live decide whether it worked
The cleanest import on earth is worthless if the team keeps working out of the old system. A field service operator commenting in r/CRMSoftware made the point that migration support "matters less than people think," because "most switches stall on the workflow gap after data is imported, not the import itself." He had seen shops move "with all their history moved over cleanly and still struggle for the first month because techs were used to doing things a certain way in the old system." His conclusion: "The bigger unlock is someone walking your actual team through their actual workflow once, not just moving the data."
That matches the broader evidence on why these projects disappoint, which we cover in why CRM implementations fail: the platform is rarely the problem. Budget the first two weeks after go-live as project time, with an owner, not as business as usual.
The bottom line
The eight steps are not the hard part, and every checklist on the internet already lists them. The hard part is that the failure mode of a CRM data migration is silence. Unmapped fields are discarded, line items collapse, timestamps reset and photos never move, and the import screen says success through all of it.
So do three things the generic checklists will not tell you. Inventory your objects and count them, because the gap between customers and service addresses and between invoices and line items is where the loss hides. Read the destination platform's own import documentation before you export anything, because it tells you plainly what it will throw away. And segment what you keep by equipment age rather than contact recency, because in this trade the ten-year-old record is the one about to become a job.
If you are still deciding whether to move at all, what switching a CRM actually costs is the more useful question to answer first. If you are moving off accounting software rather than a CRM, our QuickBooks migration checklist covers the reconciliation side in detail.
