Bulk Import from CSV
When you click Bulk import on the Users page, EUnifyer parses a CSV file and sends a set-password invitation to every row. Duplicates and invalid rows are reported back to you in the result panel; the rest of the file is processed.
File format
- First row is the header. Column order does not matter — EUnifyer matches columns by name.
- Only
emailis required. Everything else is optional. - Case-insensitive.
Email,EMAIL, andemailare all the same column. - Encoding: UTF-8.
- Quoted values supported — wrap fields containing commas in
"double quotes".
Recognised columns
| Column | Aliases also accepted | Meaning |
|---|---|---|
email | e-mail, email address, username | The user’s email (login). Required. |
first_name | firstname, first name, given_name | Given name |
last_name | lastname, last name, family_name, surname | Family name |
department | dept | e.g. Engineering, Finance |
job_title | title, role | e.g. Senior Engineer |
employee_id | employee id, emp_id | Internal HR identifier |
role_slug | — | EUnifyer role slug (e.g. admin, member, owner). Overrides the default-role picker for this row only. |
Columns that don’t match any of the above are silently ignored, so it’s safe to drop in a wider HR export without trimming columns first.
Example file
email,first_name,last_name,department,job_title,role_slug
alice@acme.example,Alice,Tester,Engineering,Senior Engineer,member
bob@acme.example,Bob,Builder,Operations,Site Reliability,member
chris@acme.example,Chris,Lead,Engineering,Engineering Manager,admin
"diana@acme.example",Diana,"O'Connor",Finance,"Director, FP&A",memberThe third row gets the admin role; everyone else falls back to the default role you picked in the dialog (typically Member).
What happens on import
For each row, EUnifyer:
- Normalises the email (lowercased, trimmed).
- Checks the org’s seat limit and refuses excess rows with an error.
- Looks for an existing user or pending invitation with that email.
- If found: Duplicate — skipped, not an error. Safe to re-run.
- If not: creates the user record, assigns the chosen role, sends a set-password email.
- Returns one of three statuses per row:
| Status | Meaning |
|---|---|
| Invited | Set-password email sent. User row is in Pending Setup until they accept. |
| Duplicate | Email already exists (as a user or a pending invitation). No action taken. |
| Error | Invalid email, seat limit exceeded, or another error. The detail column explains. |
The aggregate counters at the top of the result panel (Invited / Skipped / Errors) tell you what to do next:
- Mostly Invited → you’re done. Wait for users to accept.
- Many Duplicates → this CSV has already been imported. You can close the dialog.
- Many Errors → review the
detailcolumn, fix the file, re-run. Re-running is idempotent.
Default role
The CSV dialog has a Default role picker. Rows without a role_slug column (or with that column blank) get this role. Rows that do specify a slug always override.
Pick Member for typical employees. Don’t bulk-import people as Admin or Owner unless you mean to — those roles can manage other users, change billing, and reconfigure the workspace.
Limits
- Seats: the import respects your plan’s seat count. Once seats are exhausted the remaining rows fail with an explicit “no seats available” detail.
- File size: practical limit ~10,000 rows in one go. For larger lists, split the file.
- Personal / family plans: can’t bulk-import beyond their member cap (typically 1–6). Use individual invites instead.
Troubleshooting
“No valid rows in this file. Check that the header includes ‘email’.”
Your first row probably isn’t a header, or the email column is named something we don’t recognise. Add email as an explicit column or use one of the aliases listed above.
Several rows show “Duplicate” but I expected them to be new. Someone already invited those users — check Admin → Users with the Pending Setup filter, or Resend invitation instead of re-importing.
Errors with “Email already registered” — same as Duplicate; treat as a no-op.
Errors with “Failed to create Keycloak user” — the underlying identity service had a hiccup. Re-run the same file (skipped rows stay skipped, the failing ones will try again).
Some employees came in but never got the email — Check your domain’s SPF/DKIM if the mails went to spam. You can also use the Copy invitation link action on the row to grab a fresh link and hand it over directly.
What this is not
- Not a sync — it’s a one-time invite batch. Re-running doesn’t update existing users; for ongoing roster sync use SCIM provisioning.
- Not a permission-grant tool —
role_slugpicks the user’s role, but the actions that role is allowed to take live in Admin → Roles. The bulk import never edits role permissions. - Not for password import — users always set their own password via the link. EUnifyer never accepts pre-hashed passwords from a CSV.