Overview
Every endpoint lives under https://app.scormcentral.com/api/v1, takes and returns JSON, and wraps its result in the same envelope:
{
"success": true,
"message": "Conversion started",
"data": { ... }
}
On an error, success is false, data is null, and message is a sentence you can show to an admin as it is. Ids are 64-bit integers. Timestamps are ISO 8601 in UTC.
The usual flow is: convert a file into a course, build a SCORM package from it, publish that package as a delivery course, register a learner, and launch. The WordPress plugin does exactly this with the same API, so anything it does, your own site can do too.
API keys and modes
Send your key as a bearer token on every request:
Authorization: Bearer sc_test_…
Keys come in two modes, Stripe-style. Get them in SCORM Central under Settings › API keys.
| Key | Plan | What it does |
|---|---|---|
sc_test_… | Every plan, Free included | Up to 10 learners, who see a TEST MODE banner. Test data is deleted after 14 days. Works from your test site and from localhost. |
sc_live_… | Starter and up | Real learners on your live sites (1 on Starter, 3 on Pro, 10 on Business). |
Courses, learners, and results are kept apart by mode: a course you publish with the test key is not visible to the live key. If the subscription lapses, the live key answers 402 and the test key keeps working.
Getting a new key replaces the old key of that mode. The old one keeps working for 24 hours, so you can swap it on your server without downtime.
Your sites
SCORM Central only lets a course be framed by, and return learners to, sites you have listed. Set them in SCORM Central under Settings › API keys: one test site (localhost always works in test mode), and live sites up to your plan's limit. Sites are origins only, like https://learn.example.com; plain http is accepted for localhost only.
A key can read the list but not change it:
GET /allowed-origins?mode=test
Returns {mode, origins, max}.
Quick start
From a PDF to a learner in the course, with curl. Set SC_KEY to your test key first.
1. Convert a file. PowerPoint (.ppt, .pptx), Word (.docx), PDF, or video (.mp4, .webm, .ogg, .mov, .m4v).
curl -X POST https://app.scormcentral.com/api/v1/imports \ -H "Authorization: Bearer $SC_KEY" \ -F "file=@fire-safety.pdf" -F "title=Workplace Fire Safety"
The response is 202 with a job. Poll GET /imports/{jobId} every few seconds until status is DONE, then keep its contentId.
2. Build a SCORM package.
curl -X POST https://app.scormcentral.com/api/v1/contents/46/packages \
-H "Authorization: Bearer $SC_KEY" -H "Content-Type: application/json" \
-d '{"standard":"SCORM_2004_4","masteryScore":80}'
Poll GET /packages/{packageId} until status is READY.
3. Publish it for learners.
curl -X POST https://app.scormcentral.com/api/v1/delivery/courses \
-H "Authorization: Bearer $SC_KEY" -H "Content-Type: application/json" \
-d '{"packageId":311}'
Keep data.course.courseId.
4. Register your user. The learner id is yours: any stable id from your own user table.
curl -X PUT https://app.scormcentral.com/api/v1/delivery/courses/12/registrations/user-1042 \
-H "Authorization: Bearer $SC_KEY" -H "Content-Type: application/json" \
-d '{"learnerName":"Dana Whitfield","learnerEmail":"dana@example.com"}'
5. Launch. Do this when the user clicks Start, and redirect their browser to launchUrl straight away.
curl -X POST https://app.scormcentral.com/api/v1/registrations/8801/launch \
-H "Authorization: Bearer $SC_KEY" -H "Content-Type: application/json" \
-d '{"returnUrl":"http://localhost:8080/courses/fire-safety"}'
When the learner finishes, the player shows a Back to course link to returnUrl. Read their result with GET /registrations/8801.
Errors
| Status | Meaning |
|---|---|
400 | Validation failed. message says which field and why. |
401 | Missing, unknown, or revoked key: Invalid or revoked API key. |
402 | Live key without a paid plan. Choose a plan under Billing, or use the test key. |
403 | Your plan doesn't include this feature, or a key tried something only an admin can do. |
404 | Not found in your organisation. Another organisation's ids always return 404. |
409 | The thing isn't in the right state: a build still running, a package not ready, an archived course, or the test-mode learner limit reached. |
Imports
Conversions run in the background.
| Request | Notes |
|---|---|
POST /imports | Multipart file, optional title. Returns 202 with the job. |
GET /imports/{jobId} | {jobId, kind, title, status, progress, message, contentId, builderUrl, createdAt, updatedAt} |
GET /imports | Your last 20 jobs. |
status goes QUEUED → RUNNING → DONE or FAILED. progress runs from 0 to 100, and message is safe to show. Slides and PDF pages become one lesson of slide blocks; a Word document's headings become lessons and sections; a video becomes a tracked video lesson.
Browser uploads
For an admin screen on your site where people upload large files, don't stream them through your server. Ask for an upload token server-side, then let the browser send the file straight to SCORM Central:
POST /upload-tokens {"title":"Workplace Fire Safety"}
Returns {uploadUrl, statusUrl, expiresAt, maxBytes, accept}. The browser POSTs multipart file to uploadUrl and polls statusUrl for progress. The token is the only credential and expires after 60 minutes. Requests must come from one of your listed sites.
Courses and packages
| Request | Notes |
|---|---|
GET /contents | Your courses, newest first: {contentId, title, status, lessonCount, lastEditedAt, …} |
GET /contents/{id} | One course. |
POST /contents/{id}/packages | {standard, masteryScore}. Returns 202. 409 if a build is already running for that course. |
GET /packages/{id} | {packageId, contentId, version, standard, status, progress, findings[], downloadUrl, …} |
GET /contents/{id}/packages | Every version, newest first. |
GET /packages/{id}/download | The zip, once READY. On Free it carries the "Made with SCORM Central" watermark. |
standard is SCORM_2004_4 (default), SCORM_1_2, CMI5, or XAPI; cmi5 and xAPI need Starter or above. masteryScore is 0 to 100 and defaults to 80. Each build is a new version and a ready zip is never rewritten. findings is the validation report: {level, code, message} with level ERROR, WARN, or INFO. Any error fails the build.
Delivery courses
A delivery course is a SCORM build published for learners. Only SCORM 1.2 and 2004 builds can be delivered; cmi5 and xAPI builds are for an LMS that launches them.
| Request | Notes |
|---|---|
POST /delivery/courses | {packageId, title?}. 201 for a new course. Publishing a newer build of the same course adds a version and returns 200 with newVersion: true. |
GET /delivery/courses | ?status=ACTIVE|ARCHIVED|ALL (default ACTIVE), optional &contentId= to find the course published from one source course. |
GET /delivery/courses/{id} | The course with versions[]. |
PATCH /delivery/courses/{id} | {title}: the name learners see. |
POST /delivery/courses/{id}/status | {status: "ACTIVE"|"ARCHIVED"}. Archived courses take no new learners. |
New attempts start on the live version. A learner who is part-way through keeps the version they started on.
Learners and launch
Learner ids belong to you: 1 to 200 characters of letters, digits, and . _ : @ + -. The WordPress plugin uses wp_{site}_{user}.
| Request | Notes |
|---|---|
PUT /delivery/courses/{id}/registrations/{learnerId} | {learnerName, learnerEmail?}. Safe to call every time: 201 the first time, 200 after. Leaving out learnerEmail keeps the stored one. |
POST /registrations/{id}/launch | {returnUrl?, newAttempt?} → {launchUrl, expiresAt, attemptId, resumed}. |
GET /registrations/{id} | The learner's result on that course. |
launchUrl works once, for two minutes. Redirect the learner to it at once, and call launch again next time rather than storing it. Launch resumes the learner's current attempt unless you send newAttempt: true or that attempt has ended. returnUrl must be on one of your sites for the key's mode. A play session ends after 12 hours without activity; the learner reopens it from your site.
Results and reports
A registration rolls up all of a learner's attempts on a course, and is updated every time the course saves:
{
"registrationId": 8801, "courseId": 12, "learnerId": "user-1042",
"learnerName": "Dana Whitfield", "learnerEmail": "dana@example.com",
"completionStatus": "completed", "successStatus": "passed",
"scoreRaw": 84, "scoreScaled": 0.84, "totalTimeSeconds": 1312,
"attemptCount": 1, "firstLaunchedAt": "2026-10-31T14:02:11Z",
"lastActivityAt": "2026-10-31T14:24:03Z", "completedAt": "2026-10-31T14:24:03Z"
}
completionStatus stays completed once any attempt completes, and successStatus stays passed once any attempt passes. The score is the best score; time is the total across attempts.
| Request | Notes |
|---|---|
GET /delivery/courses/{id}/registrations | ?learnerId=&page=0&size=50 (size up to 200) → {items, page, size, total}, most recently active first. |
GET /delivery/courses/{id}/registrations.csv | Every learner on the course, one row each. UTF-8, opens cleanly in Excel. |
GET /delivery/courses/{id}/questions | Per-question breakdown: for each question, how many answered, how many got it right, and how often each option was picked. |
GET /delivery/usage | ?month=YYYY-MM (UTC, default this month) → {month, registrationsCreated, activeRegistrations}. |
Hosted links and dispatch
A hosted link lets people take a course with no site or LMS of yours: they open the link, type their name and email, and start. Hosted links need Starter or above. A dispatch is a small SCORM zip you give another organisation to import into their LMS; the course stays hosted here, under the limits you set. Dispatch comes with Business.
| Request | Notes |
|---|---|
POST /delivery/courses/{id}/access | {kind: "LINK"|"DISPATCH", name?, maxLearners?, expiresAt?} → 201. A link comes back with its url. |
GET /delivery/courses/{id}/access | The course's links, with how many learners each has. |
GET /delivery/access/{accessId}/dispatch.zip | ?standard=SCORM_1_2|SCORM_2004_4 (default 1.2). The zip to import into the other LMS. |
DELETE /delivery/access/{accessId} | Turns the link or dispatch off at once, copies included. |
Questions about the API, or something it should do and doesn't: hello@scormcentral.com.