Data Import — Switcher Program
Who this guide is for: Organization Owners (or members holding a custom role that grants Data Management) moving to EmbayLMS from another LMS who need to bring over their users, course enrollments, and historical completion records via CSV.
Overview
The Switcher Program lets you import nine kinds of data from any other LMS using plain CSV files — no per-vendor connectors. Imports are platform-agnostic: export to CSV from Moodle, Canvas, TalentLMS, Docebo, Cornerstone, Absorb, or anything else, match the columns below, and upload. The nine entities are: users, groups, group members, categories, learning paths, path courses, enrollments, completion history, and path enrollments.
You can import each entity from its own CSV, or fill one multi-tab workbook (one sheet per entity) and upload it all at once — see Multi-tab workbook below.
Every import runs in two steps:
- Validate (dry run) — we parse your file and show a preview with a per-row error list. Nothing is written until you confirm.
- Confirm — valid rows are applied in the background; you get an email when it finishes, and any failed rows can be downloaded as an error CSV to fix and re-upload.
Imported records are tagged source = import so reporting can tell historical data from
native activity, and imported completions keep their original completion dates.
Prerequisites
- The Admin or Owner role — the importer requires the
user:importpermission. - Your courses already exist in EmbayLMS with a slug (or external ID) set, so enrollment and completion rows can resolve to them.
- A CSV file (UTF-8). Excel “Save As → CSV UTF-8” works; a BOM is handled automatically.
Step-by-step
- Go to Settings → Data Management → Data Import.
- Choose what you’re importing: Users, Enrollments, or Completion history.
- Click Choose file and select your CSV, then Validate.
- Review the preview: total rows, valid rows, and any rows with errors (with the reason).
- Click Import N rows. N counts the valid rows only: rows with errors are skipped, not imported, and you can fix and re-import them afterwards. The button stays disabled when the file has a header (column) error or no valid row at all.
- Wait for the confirmation (or check Import history later). For failed rows, click Download errors to get a CSV of just those rows with their error messages.
Importing users from the Users page
If all you need is a user import, you don’t have to go through Settings — the same
importer is available directly from the Users page (Admin and Owner, user:import):
- Go to Users and click ⬆ Import CSV. This opens the user
importer at
/admin/users/import. - The record type is locked to Users — there is no type picker and no multi-tab workbook here; upload a single Users CSV.
- The flow is identical to the main importer: Validate (dry run with a per-row error preview — nothing is written), then Import N rows to confirm.
- Import history for your user imports is shown on the same page.
The file uses the same columns as the Users CSV format below — the column contract is identical between the two entry points.
CSV formats
external_id is the primary matching key; email is the fallback. Provide at least one.
Users (users)
| Column | Required | Notes |
|---|---|---|
external_id | one of | Source-LMS user ID — the stable dedup key |
email | one of | Used to match when external_id is absent; required to create a new user |
first_name | no | |
last_name | no | |
role | no | learner (default), manager, admin, or author (legacy — maps to the retired Author role; prefer the RBAC v2 roles once the import vocabulary migrates) |
locale | no | en (default) or fr-CA |
manager_external_id | no | external_id of this user’s manager (resolved after all users are created) |
group_names | no | Pipe-separated, e.g. `Sales |
job_roles | no | Semicolon-separated job role names, e.g. Site supervisor;Forklift operator. Each must already exist and be active (Skills → Job roles); an unknown name fails the row. The listed roles replace the person’s previously imported roles; roles assigned by hand or from a profile field are kept. Leave empty to change nothing. Changing a person’s roles needs a plan with skills management (Starter or higher). On other plans, a cell that lists exactly the roles the person already holds (as a users export writes it) is ignored and the rest of the row imports; a cell that adds or removes a role fails the row |
external_id,email,first_name,last_name,role,locale,manager_external_id,group_names,job_roles
e-1001,jdoe@acme.com,Jane,Doe,learner,en,e-1000,Sales|Ontario,Sales representative
e-1000,boss@acme.com,Pat,Lee,manager,en,,Sales,Sales manager;Team leadNew users are created without sending an activation email — this is a historical backfill. Invite them through normal user management when you’re ready.
Enrollments (enrollments)
| Column | Required | Notes |
|---|---|---|
external_id / email | one of | Identifies the learner |
course_slug | yes | Matched against the course slug, then external ID |
status | no | active (default), completed, dropped, expired |
due_date | no | ISO 8601, e.g. 2025-03-31 |
email,course_slug,status,due_date
jdoe@acme.com,whmis-2024,active,2025-03-31Completion history (completions)
| Column | Required | Notes |
|---|---|---|
external_id / email | one of | Identifies the learner |
course_slug | yes | Matched against slug, then external ID |
completed_at | yes | ISO 8601 — the original completion date, preserved as-is |
score | no | Numeric |
email,course_slug,completed_at,score
jdoe@acme.com,whmis-2024,2023-05-01,92A completion with no existing enrollment auto-creates a completed enrollment so the transcript and compliance evidence are intact.
Groups (groups)
Dedup key: name.
| Column | Required | Notes |
|---|---|---|
name | yes | Group name — the dedup key |
description | no | |
type | no | static (default) or dynamic |
Group members (group_members)
Maps users into groups. Import users and groups first.
| Column | Required | Notes |
|---|---|---|
group_name | yes | Must match an existing group |
external_id / email | one of | Identifies the learner |
Categories (categories)
Dedup key: slug.
| Column | Required | Notes |
|---|---|---|
slug | yes | Unique slug — the dedup key |
name | yes | Display name |
description | no | |
parent_slug | no | Slug of the parent category (for a hierarchy) |
order_index | no | Sort order (number) |
Learning paths (learning_paths)
Dedup key: title. New paths are created as draft and hidden so an import never publishes a path by surprise.
| Column | Required | Notes |
|---|---|---|
title | yes | Path title — the dedup key |
description | no | |
status | no | draft (default), published, archived |
Path courses (path_courses)
Maps courses onto a path. Import learning paths and the courses first.
| Column | Required | Notes |
|---|---|---|
path_title | yes | Must match an existing path |
course_slug | yes | Matched against course slug, then external ID |
order_index | no | Position on the path (number, default 0) |
is_required | no | true (default) / false (also yes/no, 1/0) |
prerequisite_course_slug | no | Course that must be completed first |
Path enrollments (path_enrollments)
Enrolls users onto a path. Import learning paths and users first. Path completion stays derived from course completions — this only records the enrollment.
| Column | Required | Notes |
|---|---|---|
path_title | yes | Must match an existing path |
external_id / email | one of | Identifies the learner |
status | no | active (default), completed, dropped |
Multi-tab workbook
For a full migration, use the workbook instead of nine separate uploads:
- Go to Settings → Data Management → Data Import → Multi-tab workbook and click Download workbook
template. You get an
.xlsxwith one sheet per entity, each with the right headers (your tenant’s custom user fields are included on the Users sheet). - Fill in the sheets you need — leave the rest empty.
- Upload the filled workbook. Each non-empty sheet is validated and shown with its row counts, then imported in dependency order (users → groups → group members → categories → learning paths → path courses → enrollments → completions → path enrollments) when you click Import N sheets.
The workbook headers always match the current CSV contract above — if a column is added to an import, the next template download includes it automatically.
SCORM package bulk re-upload
The CSV import brings your course records across. It does not carry the course content —
your SCORM packages are files, and every LMS stores them differently, so you re-upload the
.zip packages you already own. Settings → Data Management → SCORM Packages does that for a
whole folder at once instead of one course at a time.
What you need first
- The Owner, Admin, or Instructor role. Creating a new course needs
course:create; attaching a package to an existing course needscourse:update. An instructor sees and can attach to only the courses they are assigned to. - Your SCORM packages exported from the old LMS as
.zipfiles (SCORM 1.2, 2004 2nd/3rd/4th, cmi5 and AICC packages use the same.zipcontainer). - If you plan to attach rather than create: run the courses part of your CSV import
first, so the course shells exist with their
external_idfrom the old LMS.
Step-by-step
- Go to Settings → Data Management → SCORM Packages.
- Drag your
.zippackages onto the drop zone (or click Choose SCORM packages). Up to 25 packages per batch, 500 MB each. Files that are not.zip, are too large, or are already in the batch are listed back to you with the reason and simply not added. - Each package gets a row. Set Maps to for each one:
- Create a new course — a new draft course is created with the title shown. The title is
pre-filled from the filename (
Fire_Safety-2024.zip→ Fire Safety 2024) and you can edit it. If you leave it alone, the package’s own title from itsimsmanifest.xmlreplaces it once the file has been processed. - Attach to an existing course — pick the course from the list. Use the Search courses box above the table to narrow it; courses that came from a CSV import show their source ID from the old LMS in brackets, so you can match on that rather than the title.
- Create a new course — a new draft course is created with the title shown. The title is
pre-filled from the filename (
- Click Upload N packages. Three packages upload at a time; each row shows its own progress and then Done or Failed with the reason. A failed package does not stop the rest — fix its mapping and press the button again to retry just the failures.
- Read the summary line: N courses created, N attached, N failed.
What happens to each package
One package becomes one module on the course’s draft version — the natural unit, since a SCORM package has a single manifest. Attaching a newer export of a course therefore adds a module to the draft rather than overwriting what learners are currently taking; review the draft and publish a new version when you are happy with it.
Every package goes through exactly the same pipeline as a single upload from the course editor:
the file goes straight from your browser to encrypted storage, the server verifies its real size
against your plan’s storage allowance, checks the archive for unsafe paths and file types,
reads imsmanifest.xml for the SCORM version, launch file and table of contents, unpacks it for
the player, and queues the malware scan. Nothing about the bulk path is a shortcut around those
checks.
Configuration reference — SCORM re-upload
| Setting | Value |
|---|---|
| Accepted file type | .zip only |
| Max packages per batch | 25 |
| Max size per package | 500 MB |
| Simultaneous uploads | 3 |
| Max files inside a package | 10,000 (a package over the safety bound is refused, not stored) |
| Module created | One scorm module per package, appended to the course’s draft version |
| Course title source | Filename, replaced by the manifest title unless you edited it |
| Audit | Course creation, module creation, and the manifest rename are all written to the audit log |
Troubleshooting — SCORM re-upload
| Problem | Cause / fix |
|---|---|
| A file was not added to the batch | The reason is shown next to the filename: not a .zip, over 500 MB, a duplicate of one already queued, or the 25-file batch cap. Upload the rest as a second batch. |
| ”Select a course to attach this package to.” | The row is set to Attach but no course is chosen. Pick one, or switch the row to Create a new course. |
| ”Enter a course title.” | The row is set to Create and the title was cleared. Type one — the package’s manifest title is only applied after the upload. |
| Upload failed on one row only | Press Upload again; only the queued and failed rows are retried. Persistent failures on one package usually mean a corrupt archive — try re-exporting it from the old LMS. |
| ”Storage limit reached for your plan” | Your plan’s storage allowance is exhausted. Upgrade, or remove unused content, then retry. |
| The course kept the filename-style title | You edited the title, so we honoured yours. Clear the edit before uploading if you want the package’s own title. |
| The row failed with “No launch file was found” or “The package’s manifest names a launch file that is not in the zip” | The package could never play, so it was refused (LMS-1139): the row’s course and module were created, but no package was added. Confirm the package has an imsmanifest.xml at its root — packages zipped with an extra top-level folder often do not — re-zip from inside the content folder, then upload it to that module from the course editor. |
| A new module appeared instead of replacing the old one | That is by design — the new package lands on the draft version. Delete the superseded module in the course editor, then publish the new version. |
Configuration reference
| Setting | Value |
|---|---|
| Max file size | 8 MB |
| Encoding | UTF-8 (BOM tolerated) |
| Matching keys | external_id (primary), email (fallback); courses by slug then external_id |
| Date format | ISO 8601 (YYYY-MM-DD or full timestamp) |
| Idempotency | Re-running an import updates matched records instead of duplicating |
| Audit | Every import (create / confirm / complete) is written to the audit log |
Troubleshooting
| Problem | Cause / fix |
|---|---|
| ”Missing required column: …” | Your header row is missing a required column. Compare against the tables above (download a template from the page). |
| ”Unknown column: …” | A header doesn’t match a known column — check spelling/case. |
| ”Invalid email” / “Invalid date” | Fix the cell format (lowercase email; ISO date). |
| ”User not found” (enrollments/completions) | The learner isn’t in EmbayLMS yet — run the Users import first. |
| ”Course not found” | The course_slug doesn’t match any course slug or external ID. Set the slug on the course, or correct the value. |
| ”Learning path not found” | The path_title doesn’t match an existing path. Import the learning paths sheet first, with matching titles. |
| ”Group not found” (group members) | Import the groups sheet (or run a Groups CSV) before group members. |
| ”Prerequisite course not found” | The prerequisite_course_slug must resolve to a course that exists. |
| Some rows imported, some failed | Click Download errors on the history row to get just the failed rows + reasons, fix, and re-upload. |
| Manager not linked | The manager’s row must be present in the same (or an earlier) Users import; managers are resolved after all users are created. |
| ”No recognized data sheets in the workbook” | Sheet names must match the entity keys (users, groups, …). Use the downloaded template rather than renaming sheets. |