For more than ~5 employees, CSV import is the fastest way to populate your roster. Most onboardings start with one of these, often exported from another scheduling tool, a Google Sheet, or HR software.
The format
A standard CSV with a header row. Only name is required — everything else is optional.
name,first_name,last_name,email,phone,employment_type,designation,hourly_rate,tags
Sarah Chen,Sarah,Chen,sarah@example.com,+15551234567,full_time,Server,18.00,"Closer,Opener"
Mike Patel,,,mike@example.com,(555) 555-1111,part_time,Bartender,16.50,Closer
Tara Moss,Tara,Moss,,5555550000,part_time,Host,,
Where to find it: /dashboard/team → Employees tab → Upload CSV.
The columns
| Column | Required? | Notes |
|---|---|---|
name |
Yes | Display name shown on the schedule and in messages. |
first_name, last_name |
No | Used in {employee.first_name} template variables. |
email |
No | Required if you want them on the publish email list. |
phone |
No | Required for SMS shift alerts. Format flexibility: (555) 123-4567, 5551234567, +15551234567 all parse. |
employment_type |
No | One of full_time, part_time, contractor. Defaults to full_time. |
designation |
No | Free-text role label. Defaults to "Employee". |
hourly_rate |
No | Dollars (decimal). Stored internally as cents. |
pay_type |
No | One of hourly, salary, none. Defaults to hourly. |
tags |
No | Comma-separated tag names inside double quotes. Tags that don't exist yet are auto-created. |
notes |
No | Internal manager-visible notes. |
Unknown columns are ignored silently — so if you're exporting from another tool with extra columns, you don't need to clean them up first.
Dedupe behavior
When the import runs, each row is matched against existing employees by:
- Email (exact match) — if a row's email matches an existing employee, the row is treated as an update.
- Phone (after normalization) — same fallback if email is missing or doesn't match.
Updates change the existing employee's data; they don't create a duplicate row.
If neither email nor phone matches anything in your roster, the row creates a new employee.
If a row has no email AND no phone (just a name), it always creates a new employee — there's no way to dedupe.
The preview screen
After upload, you see a preview with three columns:
- Will create — new employees that'll be added.
- Will update — existing employees that'll have their data changed (with a diff per field).
- Errors — rows that won't import, usually because of a missing required column or an unparseable phone.
You can fix errors in the source CSV and re-upload (the preview is non-destructive — nothing's saved until you confirm).
Click Import to commit. The result is a summary: "Imported 12 new, updated 3, skipped 1."
Multi-location
The import goes into the active location (the one selected in the sidebar location picker). To populate multiple locations, run the import once per location with the appropriate roster.
If you need to bulk-move employees between locations, that's not currently supported via CSV — file a request if it'd help.
Phone-format edge cases
We use libphonenumber-js to parse phone numbers. Things that work:
- US format:
(555) 123-4567,555-123-4567,555.123.4567 - E.164:
+15551234567 - Without country code:
5551234567(assumed US/Canada)
Things that don't:
- International numbers without
+:447123456789— parsed as a US 10-digit if shorter; otherwise rejected. Always include the leading+for non-US.
Tag column gotchas
Tags are comma-separated inside the cell, with the whole cell wrapped in double quotes:
name,tags
Sarah,"Closer,Opener"
If you write Sarah,Closer,Opener (no quotes), CSV parsers split on every comma — you'll end up with Sarah in name and Closer in the next column. Quote it.
Re-importing
You can re-upload the same CSV (or an updated one) any time. Existing employees update by email/phone match; new ones get added. There's no "delete via CSV" — to remove employees, do it in the UI.