We've run this migration for real, more than once — a multi-store shipping franchise moving off Deputy, and a construction firm moving off a different workforce system. In both cases the CSV import was the easy part and took an afternoon. The hard part was the first payroll after the switch, and the traps were the same both times.
This post is the operator's version: what you'll get in the file, what you won't, and the cutover order that keeps you out of trouble. It applies whether you land on our workforce and store ops product or somebody else's — the mechanics don't care.
What a Deputy export actually contains
The export you'll work from is a user list. Per row, expect roughly:
- A display name — usually one field, first and last mashed together, sometimes with a middle initial or a nickname ("Jenny" for Jennifer).
- A role or title — the system's permission label, not your org chart. "Manager" in a scheduling tool often means "can approve timesheets", which is not the same as the person who runs the store.
- An active/inactive flag — this is the important one, and see the traps section.
- Location or team membership — often one primary location per person, even when the person genuinely works three.
- Contact fields — email and mobile, frequently blank for hourly staff who never logged into the app.
Scale-wise: one of the two exports we migrated had 32 people in it; the other carried 87 user records. Both numbers are small enough that hand-fixing is realistic, and that's worth knowing — if your roster is under about 150, do not go looking for a clever automated matcher. A person reading the list catches things no script will.
What it does not contain
Punch history. This is the one that surprises people. You are not getting three years of timesheets in a usable shape. Even when a system will export punches, they arrive as flat rows with no pairing logic, no break subtraction, and no notion of which manager edited what. Re-deriving a historical week from those rows is archaeology.
Pay rates. Rates usually live behind a separate permission, so the roster export is deliberately money-free. You will be re-entering pay from your own records — which is fine, because your own records are the authority anyway.
PTO balances. Accruals are computed by policy, and policies don't port. Balances get re-entered as opening numbers.
The published future schedule. Next week's rota stays in the old system. If you cut over mid-week, nobody knows when to show up.
So: accept that history stays where it is. The correct way to keep it is not to migrate it — it's to keep the old system alive, read-only, for as long as your record-retention rules require, and to export a PDF or CSV of the final closed pay periods into whatever folder your bookkeeper already trusts. That's cheaper and more defensible than a half-accurate import that you'll later have to argue with.
The sequence: read-only for one full pay cycle
The single biggest mistake is cutting over on a Wednesday because that's when the contract renewed. A pay week that starts in one system and ends in another has no owner. Nobody can produce a defensible total for it, and you will spend Tuesday morning reconstructing punches from memory.
Do this instead:
- Import the roster early — a week or two before cutover. Fix names, assign every person to every store they actually work, set pay, set opening PTO. Nobody is punching yet, so mistakes are free.
- Build one week of schedule in the new system in draft, alongside the old one. Compare the two grids side by side. This is the cheapest bug-catching you'll ever do.
- Cut over on the first day of a pay week. Not Wednesday. The boundary of the pay period is the boundary of the migration.
- Turn the old system read-only the same morning — revoke staff app access or at minimum stop scheduling in it, but leave managers able to read it. You want it as a reference, not as a second source of truth.
- Run one full pay cycle in parallel-read mode. New system is the only place anyone punches; old system is the thing you check against for the week that closed in it.
Reconciling the first parallel week
At the end of the first full week in the new system, don't just export payroll hours. Do a four-column check, per person, per store:
| Column | Where it comes from |
|---|---|
| Scheduled hours | The published rota in the new system |
| Actual hours | Paired punches, breaks subtracted |
| Manager-edited punches | The audit trail — every fix, who made it |
| What you'd have paid last week | Your last payroll run out of the old system |
You are looking for three specific discrepancies, and each has a distinct cause:
- Scheduled and actual differ by roughly a full shift. Usually a missing punch, not a no-show. Check for an open shift with no out.
- Actual is a clean 4–5 hours off on some days. That's a timezone bug, not a person. Every punch has to be interpreted in a real timezone with daylight saving, not stored raw and hoped over — we hit exactly this and it shifted punches by hours. The time clock mechanics post covers why.
- Somebody's hours landed at a store they didn't work. That's the multi-store trap, below, and it's the one that costs money.
Then, and only then, produce the numbers your bookkeeper works from. Ours builds a weekly workbook with hours and overtime pulled from paired punches, split per store, with the leave columns left blank for hand-fill. It is a workbook for a human to check and file — not a payroll processor. It does not file taxes and it does not move money, and you should be suspicious of any migration plan that assumes the new system will quietly do both.
Migrating in the next quarter? If you want to see this reconciliation run against your own roster and your own store list before you commit to anything, book a demo and bring the export file.
Four traps that bite every time
1. Terminated staff import as active
The export has an active/inactive column; the importer, by default, doesn't care. Every person who ever worked for you arrives as a live employee. On a roster of 87 that can be a third of the file. The consequences are not cosmetic: ex-staff show in the scheduler's picker, appear in headcount, and — if you're billed per active employee, as workforce products commonly are — you pay for them. Map the inactive flag explicitly to a terminated status on the way in, and reconcile headcount against your own list before you finish.
2. Names don't match between systems
Your scheduling export, your payroll sheet, and your POS will each spell a surname slightly differently. In a real import we hand-fixed a set of spelling variants — a transposed consonant in a surname, a person listed by last initial in one system and by full surname in the other, a first name spelled two ways. Two people existed in the payroll sheet and not in the export at all, and had to be created from scratch.
The practical rule: match on something that isn't a name. Mobile number is usually the best available key. Where you must match on names, normalise (lowercase, strip punctuation) to generate candidates, then have a human confirm each one. Never auto-merge on a fuzzy name score — a wrong merge puts two people's hours in one bucket, and you will find out on payday.
3. Multi-store people arrive with one store
Exports typically carry a single primary location. But in a franchise, a good associate covers three. If the import gives everyone exactly one store, the scheduler can't put them anywhere else, and their hours file against the wrong location. Treat "home store" and "stores they can work" as two different fields, and fill both on the way in. This is the single most common cause of a broken first week — enough that it gets its own post on what actually breaks past four or five stores.
4. The role label isn't your org chart
Re-derive permissions from scratch. It takes twenty minutes and it's the only chance you get to notice that three former managers still have approval rights. If your new system has per-area permissions, assign stores to every manager explicitly — a manager with no stores assigned should see nothing, and a manager with all stores assigned by accident sees everyone's pay-adjacent data.
When you shouldn't switch at all
Honest answer: if you run one location, everyone is hourly, nobody works two stores, and your current tool's timesheet already matches what you pay — you don't have the problem this migration solves. Deputy and its peers are competent at single-site scheduling. Switching will cost you a pay cycle of attention and buy you very little.
The switch pays for itself when your workforce tool and your other operations are separate systems that don't agree: when the same person's hours have to split across store entities for payroll, when a kiosk on the counter is already doing other jobs and could just as well take the punch, or when your bookkeeper is retyping a CSV into a workbook every Monday. That's the shape we built for — see how we compare to Deputy feature by feature, and what the franchise rollout actually looked like.
One last piece of advice that costs nothing: before you import anything, save the raw export file somewhere immutable with the date in the filename. Every migration involves at least one moment where you need to know what the original file said, and by then the old system's access has lapsed.