An LMS migration succeeds or fails on the records, not the courses. Completion dates, certificate expiry dates and the history of who changed what have to arrive intact, and you have to prove it before the old system goes read-only.
A course can be rebuilt next month. A completion date lost on import can’t, and the person who finds the gap may well be an auditor. This guide covers the fields each record needs, what each export format keeps, where SCORM re-imports break, and the go/no-go test before the old LMS closes.
If you’re still standing up the new platform, the LMS implementation plan sets the go-live gates. This guide is the migration work inside them.
Key Takeaways
- A completion record needs seven fields to survive the move, and an import that writes today’s date as the completion date resets every recertification clock in the same week.
- Audit history doesn’t travel with completions unless someone asks for it: Moodle’s course backup, for one, lets the person running it choose whether to include user completion details, course logs and grade history.
- Exporting your SCORM library moves zero completions, and a package that plays in the new LMS can still record nothing.
- Keep the old LMS read-only, not deleted, until a written go/no-go test passes, and decide before cutover what happens to new completions if you roll back.
Not every old course deserves a migration. The short, out-of-date ones are faster to rebuild from the current policy document on an AI LMS than to repair.
What a Migrated Completion Record Has to Carry
The courses are the part of a learning management system you can rebuild. The completion rows aren’t. Before you request an export, write down what the new system needs from each record: it has to report on it, send reminders from it and answer an auditor about it. Seven fields do that work.
| Field | Why it has to come across | What goes wrong without it |
|---|---|---|
| Learner identifier | Joins the record to one person in the new system | The record lands on nobody, or on a duplicate |
| Course identifier, mapped old to new | Joins the record to a course that exists in the new system | History for retired courses has nowhere to live |
| Completion date, as originally recorded | Starts every due date and expiry calculation | Import day becomes the completion date |
| Status and score | Separates “finished” from “passed” where that matters | A failed attempt reads as complete |
| Certificate issue date | Shows when the credential was earned | A reissued certificate carries today’s date |
| Certificate expiry date or renewal interval | Drives the reminder and the re-enrollment | The certificate becomes a static fact that never expires |
| Course version or issuer | Shows which version of the course, or which outside body, the record belongs to | Nobody can tell who trained on the old policy |
The course identifier needs a mapping table of its own, one row per old course with its new ID. A retired course still needs a home. Create an archived placeholder in the new system for anything whose history you must keep, so those completions have a course to point at.
The expiry field only helps if the new system acts on it. Some platforms re-enroll a person when a certificate expires, and others just re-send the course. The compliance LMS comparison sorts them on exactly that.
The import-date trap
Check whether the import tool takes the completion date from your file or sets it to the day the record was created. It’s the single field where one wrong setting damages every record at once.
For illustration: 1,200 employees finished an annual safety refresher at different points over the past year. If the import writes the migration date, March 2, as every completion date, all 1,200 certificates expire in the same week next March. Every reminder and re-enrollment lands on one team at once. After the trial import, pick five people whose completion dates you know and confirm they didn’t move.
Write every date in the export year-month-day, the ISO 8601 way: 2026-03-02. Written as 03/02/2026, the same date means March 2 in the US and February 3 in most of Europe, and an import tool will pick one reading without asking you.
People who left
Bring the history of leavers, not their accounts. An audit of last year’s training asks about everyone who was employed last year. Import leavers as deactivated learners, and ask the new vendor in writing whether deactivated learners with history count as users on your plan.
Audit History Is a Separate Export
A completion record says what happened. The audit history says who made it happen: who assigned the course, who granted an exemption or an extension, who edited a score, and when. An auditor who finds a completion dated before its assignment will ask for that history. It doesn’t travel with the completions unless someone asks.
Moodle, for one, lets the person running a course backup choose whether to include user completion details, course logs and grade history. A backup that holds the completions doesn’t prove the logs came with it, so ask what went in. Whatever the platform, put three questions to the outgoing vendor in writing:
- Does the export include the change log for completions, scores and certificates, or only their current values?
- How far back does the log go, and does it include admin edits and deletions?
- Can we keep read-only access to the old system after the term ends, and for how long?
If the new system can’t take the log as data, don’t force it. Store it as a read-only export where compliance can find it, with a named owner, for as long as your longest retention requirement runs. Your compliance or legal lead has that number.
What a “Full Export” Contains: Four Formats
Ask a vendor whether you can export your data and the answer is yes. Ask what the file contains and the answers split into four formats, each keeping different things.
| Format | What it keeps | What it drops | Who can read it |
|---|---|---|---|
| CSV or spreadsheet of completions | One row per learner per course: dates, status, score | Attempt detail, context, and who recorded or changed the row | Any system with an import template |
Vendor backup file, such as Moodle’s .mbz course backup |
Course structure plus whichever user data was included at backup | Nothing that was included, but it’s in that platform’s own format | That platform’s restore tool, unless the new vendor confirms it reads it |
| xAPI statements from a learning record store | Actor, verb and object, any result, context and timestamp, and the authority that asserted the statement | Nothing in the statement, but reporting on it needs a store or a mapping | A learning record store, or a team that can report from one |
| SCORM packages | The course and its tracking rules | Every learner’s attempts, scores and resume state | Any LMS that accepts the version |
The last row is the one teams misread. Exporting your SCORM library moves zero completions. The records come out through the first three formats or not at all.
Under the xAPI specification, a statement can carry context, and the record store gives every statement it stores an authority. A CSV row has no column for either. If your old system keeps statements, export them as well as the CSV, even when the new system only imports the CSV. The statements are the fuller archive, and how xAPI records differ from SCORM explains what’s in them.
Don’t count on downloading the original SCORM zips back out of the old LMS, either. Check whether it hands back the file you uploaded before you rely on it, and collect the source zips from your authoring tool or shared drive either way. Re-export anything that lives only inside the old system while you still have access.
The rule for the request: name the format, the fields and the date range, and ask for a 50-row sample before the full run. A sample with an empty expiry column is a problem you can still fix.
SCORM Re-Import: Where a Package That Plays Still Breaks
If you’re the instructional designer repackaging or re-testing the files, this section is yours.
Importing a SCORM package into a new LMS proves one thing: the zip opened. Whether the new system records a completion is a separate question, and three things decide it.
The version string picks the runtime
The version a package claims is a line of text in its manifest, and the platform matches that text against a short list of exact strings. Moodle’s SCORM import code accepts 1.2, 1.3, CAM 1.3, 2004 3rd Edition and 2004 4th Edition. Anything else runs under SCORM 1.2, with no error at import. A package built for 2004 then calls cmi.completion_status and cmi.success_status in a runtime built around cmi.core.lesson_status, and those calls fail.
The course plays and reports nothing. If your library mixes versions, read the manifest line of every package before the bulk import. The SCORM 1.2 vs 2004 guide shows where that line sits and which three reasons justify 2004.
In-progress attempts restart
Resume state, the record of where a learner stopped, is stored by the LMS, not inside the package. It stays behind in the old system. A learner halfway through a 40-minute module on cutover day opens it in the new LMS at the first screen.
Set a finish-by date for assigned courses one week before the old system goes read-only. Tell learners in the same message that unfinished attempts will restart.
“It plays” is not “it records”
Test each package with a test learner in the new system: finish it, then read the report for a status, a score and a date. A validator outside your LMS catches packaging faults first, and the two-stage SCORM test covers both stages.
For a large library, draw the line here. Test every package attached to a certificate or a due date, plus one package from each authoring tool that produced the rest.
Duplicate Learners and the Overlap Period
Duplicate learners come from one cause: two systems matching people by different keys. Say the old LMS knows someone by employee ID and the HRIS sync into the new one matches on work email. The first sync creates a second profile, with an empty history, beside the migrated one. That person now shows as overdue on everything they’ve already finished.
Email is the usual key and a messy one. Case differences, plus-aliases and contractors with two addresses all produce duplicates, as the LMS integration guide explains. Three rules keep the overlap clean:
- Agree one learner key with IT and the HRIS admin before the first import, and use it in both the migration file and the sync.
- Import history before you switch on the HRIS sync, so the sync finds existing learners instead of creating new ones.
- Run a duplicate report after every import batch, and fix any key, name or email that appears twice before the next one loads.
Keep the overlap short. Every day both systems accept completions is a day of records someone reconciles by hand. Stop new assignments in the old LMS on a fixed date and send every new enrollment to the new system from then on.
The LMS Migration Project Plan, Phase by Phase
The plan below covers the records, from the old contract to the day the old system closes. Each phase ends on a test, not a date.
| Phase | What happens | Owner | Done when |
|---|---|---|---|
| 1. Contract | Read the old contract’s renewal and termination terms, confirm read-only access after the term | L&D lead, procurement | Notice date and read-only end date are in the plan |
| 2. Inventory | Count learners, completions, certificates and courses, then triage courses into migrate, rebuild, retire | LMS admin | Counts signed off, every course in a column |
| 3. Export request | Name formats, fields and date range, ask for a 50-row sample | LMS admin | Sample opens and every field is populated |
| 4. Map and clean | Old-to-new course table, one learner key, ISO 8601 dates, leavers flagged | LMS admin, HRIS admin | No unmapped course IDs, no duplicate keys |
| 5. Trial migration | One department imported into the new system’s test environment | LMS admin | Spot-check passes and fix time per 1,000 rows is known |
| 6. SCORM test | Version strings read, packages tested with a learner | Instructional designer | Status, score and date appear for every tested package |
| 7. Freeze and full import | New assignments stop in the old LMS, history imported, sync switched on | LMS admin | Counts match the inventory, minus written exclusions |
| 8. Go/no-go | Cutover test run, old system set to read-only | Sponsor | Every go/no-go test passes |
| 9. Archive and close | Change log and full exports stored, old access ends | Compliance lead | Archive has a named owner and a retention period |
Start with the old contract, since it sets the latest date for everything else. SkyPrep’s published terms show how a renewal clause works. The agreement renews for a term of the same length unless either party gives written notice “no less than sixty (60) days” before the term ends. Find your own clause and put the notice date at the top of the plan.
If the old system is one you host yourself, the exit works differently, and the hosted vs self-hosted comparison covers both cases.
Size the job with a trial batch
Don’t budget the migration from anyone’s published average. Time a trial batch of your own records, because the hours go into fixing bad rows, and only your rows show how many there are.
For illustration: a trial import of one department’s 2,000 completion rows takes three hours to map, load and fix. Of those rows, 60 (3%) fail the spot-check. The full export is 48,000 rows, 24 times the trial. At the same rate that’s 72 hours of fixing and about 1,440 rows corrected by hand.
That number is the decision. Either fix the mapping and run a second trial until the failure rate drops, or book 72 hours of someone’s time before the freeze date.
Rollback: The Decision You Make Before Cutover
A backup isn’t a rollback plan. Once learners finish courses in the new system, rolling back means deciding what happens to those completions. That’s a hard call on the day something breaks. Write four things down before cutover:
- The trigger: the specific failure that sends you back, such as completions missing from the report for a course with a certificate attached
- The deadline: the last day rollback is allowed, no more than a week after cutover, so the records to reconcile stay few
- The new completions: whether records made in the new system get re-entered in the old one or kept as a dated export until the second attempt
- The owner: the person with authority to call it, reachable on cutover day
“It feels wrong” isn’t a trigger, and after the deadline you fix forward in the new system. The old system stays read-only, not deleted, until that deadline passes. Read-only is what makes rollback possible: nothing new lands there, so that side has nothing to reconcile.
Cutover Day: The Go/No-Go Test
Cutover is the day the old LMS stops accepting records and the new one becomes the only place training is recorded. It goes ahead when every test below passes. The owner in each row runs it and writes the result down.
| Test | Passes when | Owner |
|---|---|---|
| Record counts | Learners, completions and certificates in the new system match the inventory, minus a written list of exclusions | LMS admin |
| Field spot-check | 25 learners match the old records field for field, including five leavers and five certificates due to expire within 90 days | L&D lead |
| Expiry reminder | A test certificate set to expire next week sends its reminder and re-enrolls the learner | LMS admin |
| SCORM records | Every package tied to a certificate or due date, finished by a test learner, shows status, score and date | Instructional designer |
| Duplicates | The duplicate report is empty and active accounts match the HRIS headcount | HRIS admin |
| Audit archive | The old system’s change log and exports are stored with an owner and a retention period | Compliance lead |
| Rollback | Trigger, deadline and owner are written, and the old system is read-only | Sponsor |
If one test fails, the date moves, not the test. A migration that goes live with a failed count is one where nobody knows which records are missing.
Add one line to the cutover message for learners: what to do if their history looks wrong, and who fixes it. The first week of questions is your last spot-check, and after that the system belongs to whoever owns LMS administration.
The LMS Migration Checklist
Copy this into your project tool. Each item is either done or not.
Before you give notice
- [ ] Renewal and termination clauses read, notice date in the plan
- [ ] Read-only access after the term confirmed in writing
- [ ] Export formats, fields and change log confirmed with the old vendor
Inventory and export
- [ ] Learner, completion, certificate and course counts recorded
- [ ] Courses triaged into migrate, rebuild and retire
- [ ] 50-row sample export opened, every field populated
- [ ] Source SCORM zips collected outside the old LMS
- [ ] xAPI statements exported, if the old system stores them
Map and clean
- [ ] Old-to-new course ID table, with placeholders for retired courses
- [ ] One learner key agreed with IT and the HRIS admin
- [ ] Every date in ISO 8601 (YYYY-MM-DD)
- [ ] Leavers flagged for import as deactivated learners
Trial and SCORM
- [ ] Trial batch imported with original completion dates intact
- [ ] Fix time per 1,000 rows measured
- [ ] Manifest version string read for every package
- [ ] Every certificate-linked package tested with a learner, report checked
- [ ] Finish-by date sent for courses in progress
Cutover
- [ ] New assignments stopped in the old LMS
- [ ] History imported before the HRIS sync is switched on
- [ ] Duplicate report empty
- [ ] Go/no-go tests passed and signed
- [ ] Rollback trigger, deadline and owner written
After cutover
- [ ] Old system read-only until the rollback deadline
- [ ] Change log and exports archived with an owner and a retention period
- [ ] Old access closed on the date in the plan
Set the Read-Only Date, Then Work Backward
Put two dates in the plan today: the old contract’s notice date and the day the old LMS goes read-only. Everything else works backward from them.
This week, ask the outgoing vendor for a 50-row sample export. Check that it carries original completion dates and certificate expiry dates, and ask in writing whether the change log comes with it. If the sample is missing any of the three, that’s your first problem, and it’s far cheaper to find now than on cutover day.
Mini Course Generator is our product. It exports courses as SCORM or PDF at any time, and the pricing page shows which plans include dynamic SCORM export. Its SCORM Upload Block places a package built in another LMS or authoring tool inside a course and tracks how learners interact with it. It doesn’t import another platform’s completion history, so that history belongs in the archive this plan builds.
FAQ
How long does an LMS migration take?
It depends on how many rows need fixing more than on how many courses you have. Time a trial batch of one department’s records, measure fix time per 1,000 rows, and multiply by the full export. Add the old contract’s notice period and a rollback window after cutover, and the plan has an honest date.
Can we move SCORM courses without re-testing them?
You can, but you won’t know a package records nothing until a certificate report comes back empty. Test the packages tied to a certificate or a due date first; the SCORM re-import section above covers the version string and the test.
Do we have to migrate records for people who left?
Their history, yes. Their accounts, no. Get the new vendor’s answer on whether deactivated learners count as users before the import, because it changes what the plan costs, not how you migrate.
What happens to courses learners haven’t finished?
Learners start them again. Progress doesn’t transfer, so your only lever is time: send a finish-by date a week before the old system goes read-only.
Should we run both LMSs in parallel?
Briefly, and only one of them should accept new completions at a time. Stop new assignments in the old system on a fixed date. Keep it read-only as the reference and the rollback option, then close it once the rollback deadline has passed.
Sources
- Moodle SCORM documentation and SCORM module – recognized version strings and the SCORM 1.2 fall-through
- Moodle course backup documentation – choosing whether a backup includes user completion details, course logs and grade history
- ADL Initiative – xAPI specification, statement data model
- SkyPrep terms of service – renewal and non-renewal notice
- ISO 8601 – International Organization for Standardization date and time format



