Spreadsheet to CRM migration: the agency playbook
The spreadsheet to CRM migration playbook for agencies: sheets as your schema draft, tiered import, the two-week parallel run, and killing the sheets.
TL;DR
Every agency runs on spreadsheets until the day the spreadsheets run the agency — usually somewhere between fifteen and twenty-five clients, when the client list, the tracker tabs, and FINAL_v3_USE THIS ONE stop being a system and start being a liability. The migration playbook: inventory the sheets first — they're your accidental schema, and the columns people actually maintained tell you what your agency truly tracks. Triage each sheet into records (move to the CRM), documents (move to the wiki), or junk (archive, don't migrate). Adopt the destination's shape instead of recreating your sheet inside it — the tool's opinions are what you're buying. Import in tiers: active clients with full history, dormant ones as name-and-notes stubs. Run two weeks in parallel with the sheets demoted to read-only, cut over on a quiet Tuesday, and then actually kill the sheets — archived, locked, linked from the new system. And treat every new spreadsheet that appears afterward as a feature request in disguise: it's the team telling you what the system still doesn't hold.
This is the generalized version of a story we've told once before in tool-specific form — switching from HubSpot covers leaving a CRM for another CRM. This one covers the more common agency origin story: leaving no system — which is to say, leaving spreadsheets — and it's different, because spreadsheets don't export a schema. They are the schema, written in habit.
Step 1 — Inventory: your sheets are a schema draft
Before touching any import, list every operational sheet: the client master list, the per-client trackers, the password sheet (we'll get to it), the status tab someone built in 2024 that one department still secretly updates. For each, note who maintains it and which columns are actually filled.
Read the inventory as a design document, because that's what it is. The columns people maintained are your real data model — the fields your agency genuinely tracks under deadline pressure. The columns that are 80% empty are aspirations; don't migrate aspirations. This inventory is also your scope contract for the whole project: anything not on it doesn't move.
Step 2 — Triage: records, documents, or junk
Each sheet (or tab, or column group) gets exactly one label:
- Records — clients, contacts, retainer facts, tracking IDs, task lists: structured data with rules. This is what migrates to the system of record.
- Documents — the SOP living in a tab, the meeting-notes column, the brief pasted into a cell: prose wearing a grid costume. It moves to the wiki layer (Notion's actual job), not the CRM.
- Junk — the 2023 prospect list, the abandoned tracker, anything nobody could name an owner for. Archive it read-only. Migrating junk launders it into the new system with fresh credibility it never earned.
And the special case: credentials get their own exit. The passwords tab goes to a password manager with per-client vaults — never into the CRM, never into another sheet. Same rule as the asset-management post: different sensitivity, different tool, no exceptions.
Step 3 — Map to the destination's opinions (don't rebuild the sheet)
The single most common migration failure: recreating the spreadsheet inside the new tool — custom fields for every old column, the same flat shape, now with login friction. You bought the tool for its opinions: clients as first-class records, contacts attached to them, tasks with departments and due dates, tracking infrastructure as typed nodes instead of a GA4 ID column that nothing validates.
So map columns to the destination's native shape, and let the leftovers earn custom fields one by one — each with an owner and a reason. A migration that ends with forty custom fields didn't migrate; it relocated. (This mapping conversation is also the best possible trial-week exercise if you're still choosing the destination.)
Step 4 — Tiered import: history is not all worth carrying
Full-fidelity for the clients that are alive; stubs for the ones that aren't:
- Tier 1 — active clients: everything. Contacts, open work, retainer facts, tracking IDs, the notes that explain the landmines. This is where import care pays compounding interest.
- Tier 2 — dormant but real (might return, referenced occasionally): name, key contacts, a closing note, a link to the archived sheet. Ten minutes each, not an afternoon.
- Tier 3 — historical: they live in the read-only archive, findable but not migrated.
The tier structure is what keeps the project at days instead of months — and it front-loads the payoff, because Tier 1 is the only tier the team touches daily.
Step 5 — The parallel run, the cutover, and the funeral
Two weeks, sheets demoted to read-only, all new writing in the new system. The parallel run exists to surface what the inventory missed — the column someone maintained in their head, the workflow nobody documented — while the old world is still warm. Then a quiet-Tuesday cutover (the HubSpot playbook's timing logic generalizes): announce it, link the archives from the new client records, and lock the sheets.
The lock matters more than it seems. A spreadsheet that stays editable is still the system for whoever edits it, and split-brain — half the truth in each place — is worse than either system alone. Hold the funeral properly.
Afterward: every new sheet is a feature request
Six weeks later a new spreadsheet will appear. Don't ban it — read it. A sheet that materializes next to a system of record is the team reporting, precisely, a job the system isn't holding: a missing view, an unmodeled field, a workflow that needs a home. Sometimes the answer is configuration; sometimes it's a genuinely sheet-shaped side task (fine!); sometimes it's the signal you bought the wrong shape — in which case the buyer's guide is still there.
The migration isn't from spreadsheets to software. It's from memory to record — and the agencies that make it stop re-answering the same question every week: "wait, which tab is the current one?" None of them. That's the point.