Short answer
The single SCORM API value that breaks more “Why is this not completing?” tickets than any other. What it means, how authoring tools set it, and the safe default.
A client emails at 9pm: “Everyone finished the course but the LMS still says incomplete — did you build it wrong?” You open the package, click straight to the last slide, watch the completion screen appear, and it looks perfect. So you reply “works on my end,” and you are both now stuck. The course runs. The completion just never registers. More often than not the culprit is a single SCORM value: cmi.completion_status.
It is the most misunderstood field in the whole SCORM envelope, and it breaks more “why is this not completing?” tickets than the rest of the spec combined. So let’s take it apart: what it means, what it is not, how each authoring tool sets it, how to read the live value in thirty seconds, and the safe default that makes most of these tickets disappear.
Completion is not the same as passing. cmi.completion_status answers “did they finish?” — never “did they pass?”/01What the value actually is.
cmi.completion_status is a SCORM 2004 data-model element. The course writes it through the runtime API (SetValue), the LMS reads it back, and that one string is what decides whether a learner’s record flips to “complete.” The spec allows exactly four values, and nothing else is legal:
completed— the learner finished the experience.incomplete— they started but haven’t reached the end yet.not attempted— they’ve never meaningfully engaged with the SCO.unknown— the runtime can’t determine status. This is the one that ruins your evening.
Here is the part people miss: a course that never explicitly writes completed will sit at incomplete (or unknown) forever, no matter how many times a learner clicks to the final slide. Completion is an assertion the course has to make, not something the LMS infers from the last page being visited. If the trigger that fires that assertion is on the wrong slide, behind a button no one clicks, or never authored at all, the value never changes.
/02Completion is not success.
The single biggest source of confusion is that people treat “completed” and “passed” as the same event. In SCORM 2004 they are two separate fields with two separate jobs:
cmi.completion_statusanswers “did they finish?” — valuescompleted/incomplete.cmi.success_statusanswers “did they pass?” — valuespassed/failed.
A learner can finish a quiz and fail it: that’s completed + failed, which is a completely valid combination. SCORM 1.2 couldn’t express that nuance — it crammed everything into one field, cmi.core.lesson_status, whose values (passed, failed, completed, incomplete, browsed, not attempted) mix the two concepts together. That single design decision is why so many courses behave differently across SCORM versions. When you migrate a course from 1.2 to 2004, you are splitting one field into two, and any logic that assumed they were the same thing breaks.
/03How each tool sets it.
You rarely write cmi.completion_status by hand — your authoring tool does it, based on a “completion trigger” you configure at publish time. The defaults differ in ways that quietly cause tickets, so here’s what each common tool does.
Option 01
Articulate Storyline.
Trigger: a slide visited, a result slide, or a custom JavaScript call
In the publish dialog, the Reporting and Tracking options let you complete by number of slides viewed, by a result slide’s pass/fail, or via a “Course is complete when the user…” trigger you wire yourself. The classic bug: tracking is set to a result slide the learner can finish the course without ever reaching, so completion never fires.
- Works well
- Completion trigger is an explicit, visible setting at publish time
- Can complete on a result slide, a specific slide, or your own trigger
- Publishes cleanly as either SCORM 1.2 or 2004
- Watch out for
- Default trigger is often “last slide viewed” — easy to point at the wrong slide
- A custom completion trigger that never fires fails silently
Option 02
Articulate Rise.
Trigger: percent of content viewed, or quiz pass
Rise keeps it deliberately simple: complete when the learner views a set percentage of the lesson, or when they pass a quiz. That removes the wrong-slide trap but introduces a subtler one — a learner who scrolls fast can trip a percent-viewed completion without actually engaging.
- Works well
- Completion is a simple percent-of-blocks-viewed or quiz-pass toggle
- Hard to point at the “wrong slide” — there are no slides to mis-target
- Good default behavior for linear content
- Watch out for
- Percent-viewed completion can fire before a learner reads anything
- Less control when you need a precise, custom completion moment
Option 03
Adobe Captivate.
Trigger: slide views, quiz, or a project completion setting
Captivate gives you completion by slide views or by quiz result. The frequent failure mode is publishing as one SCORM version and hosting on a runtime that expects the other — the completion call goes out, but to a field name the host isn’t listening on, and the status stays unknown.
- Works well
- Flexible completion criteria (slide views, quiz, or both)
- Granular reporting options for compliance work
- Watch out for
- Version mismatches between published package and host bite hard here
- Quiz-pass completion sometimes conflates success with completion
/04Read the value in 30 seconds.
Stop guessing. You can read the live completion status straight from the browser console while the course is open, which tells you instantly whether the problem is the course (it never wrote completed) or the host (it isn’t storing what the course wrote). Open the module, finish it, then open DevTools and ask the running API directly.
SCORM 2004 · read completion + success from the console
// Find the SCORM 2004 runtime the course is talking to.
// (1.2 courses expose "API"; 2004 courses expose "API_1484_11".)
var api = window.API_1484_11 || window.top.API_1484_11;
// What did the course actually assert?
api.GetValue("cmi.completion_status"); // "completed" | "incomplete" | "not attempted" | "unknown"
api.GetValue("cmi.success_status"); // "passed" | "failed" | "unknown"
// Force a terminal state to confirm the host stores it, then reload.
api.SetValue("cmi.completion_status", "completed");
api.Commit("");If GetValue returns completed but the LMS report still says incomplete, the course is doing its job and the host (or a sync delay) is the suspect. If it returns incomplete or unknown after you genuinely finished, the course never fired its trigger — go back to the publish settings. This one check collapses most arguments between an ID and an LMS admin into a five-minute fix.
The “API not found” trap
One more wiring gotcha worth naming: SCORM courses search the parent window for the API during page load. If your host injects that API after the course has finished looking, you get a silent “API not found,” and nothing the course writes — including completion — is ever recorded. A good host injects the API before the course frame loads. If GetValue throws because api is null, that’s your sign.
/05The safe default.
After enough of these tickets, the prescription gets short. For new content, default to SCORM 2004 4th Edition: it separates completion from success, gives you a far larger suspend_data budget for resume, and supports a completion threshold so you can tie completion to real progress instead of a single slide. Then:
- Set the completion trigger to something a learner must reach — a final result slide or a true end screen, not “last slide in the menu.”
- Keep completion and success distinct. Don’t mark a quiz “complete” only on a pass unless that’s genuinely your rule — failing learners still finished.
- Match your publish version to your host. If you host on a platform that runs both SCORM 1.2 and 2004 in the browser — like a purpose-built hosting service; Training OS plays both through Allure Connect — the version-mismatch failure mode largely goes away. (xAPI support in Training OS is planned, not live.)
- Before you ship, read the live value from the console once. Thirty seconds now saves the 9pm email later.
Completion status isn’t mysterious once you see it for what it is: a single string the course has to assert and the host has to store. Get the trigger right, match the versions, and verify the value — and “why is this not completing?” stops being a recurring ticket. If you’re still untangling where to put the course in the first place, the SCORM hosting walkthrough covers the four real options.
/06Frequently asked questions.
What’s the difference between cmi.completion_status and cmi.core.lesson_status?
cmi.core.lesson_status is SCORM 1.2 and folds completion and pass/fail into one field. cmi.completion_status is SCORM 2004 and tracks only completion; success lives in a separate cmi.success_status field. Upload a 2004 course to a 1.2-only host and the names never match, so completion silently never fires.Why does my course say incomplete even after I finish it?
What values can cmi.completion_status hold?
Should I author in SCORM 1.2 or 2004 to avoid completion bugs?
suspend_data budget, and supports completion thresholds. Keep a SCORM 1.2 export on hand only if a specific client LMS demands it.See what Allure Training OS includes
About the author
Jonathan Borgwing
Jonathan Borgwing founded Allure Training OS. He writes about hosting SCORM, xAPI and sharing work clients can open.