scorm
12 endpoints.
/api/scorm-courses/{course_id}/attempts Bearer tokenList a course's attempts
Lists every learner's attempts of one course, newest first, one page at a time — each joined with the learner's current profile. Requires scorm:read over the course's owner.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| course_id | path | string (uuid) | yes | UUID id of the SCORM course. |
| limit | query | integer | no | Page size, 1-200. Defaults to 50. |
| cursor | query | string | no | Opaque cursor from a previous page's next_cursor. |
Responses
| Status | Description | Body |
|---|---|---|
| 200 | One page of the course's attempts. | ScormCourseAttemptList |
| 400 | The cursor is not one this API issued. | ErrorResponse |
| 401 | The access token is missing or invalid. | ErrorResponse |
| 404 | No visible course has that id. | ErrorResponse |
| 500 | The database could not be read. | ErrorResponse |
/api/scorm-courses/uploads Bearer tokenMint an upload ticket for a SCORM package
Issues a presigned upload URL for one SCORM package (.zip), bypassing the request-body limit by sending the bytes straight to the private blob store. The signed ticket pins a server-chosen pathname, zip content types and the package size cap. The staged upload becomes a course only through POST /scorm-courses, which validates the package first. Requires scorm:write over the intended owner — the organization, or the owning department within it when one is named.
| Field | Type | Required | Description |
|---|---|---|---|
| org | string | yes | Organization UUID id or slug the course will belong to. at least 1 character |
| department | string | no | UUID id or slug of the owning department within the organization; omit for a course owned by the organization directly. at least 1 character |
| file_name | string | yes | 1–255 characters |
| size_bytes | integer | yes | Declared package size; the upload token enforces the cap. maximum 314572800 |
Responses
| Status | Description | Body |
|---|---|---|
| 201 | The presigned upload ticket. | ScormPackageUploadTicket |
| 400 | The request body failed validation. | ErrorResponse |
| 401 | The access token is missing or invalid. | ErrorResponse |
| 403 | The caller does not hold scorm:write over the intended owner. | ErrorResponse |
| 404 | No visible organization has that id or slug, or the department is not one of the organization's own. | ErrorResponse |
| 500 | The upload ticket could not be issued. | ErrorResponse |
| 503 | File storage is not configured on this deployment. | ErrorResponse |
/api/scorm-courses Bearer tokenList SCORM courses
Lists the hosted SCORM courses the caller's scorm:read scope covers, newest first, optionally filtered to one organization or one owning department within it. Learners list what they can take through GET /scorm-courses/available instead.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| org | query | string | no | Organization UUID id or slug to filter by (and to resolve slug refs in). |
| department | query | string | no | Department UUID id or slug, resolved within org (which is then required). |
| include_archived | query | string | no | Include archived rows. Defaults to false. |
Responses
| Status | Description | Body |
|---|---|---|
| 200 | The visible courses. | ScormCourseList |
| 400 | The department filter was given without an org filter. | ErrorResponse |
| 401 | The access token is missing or invalid. | ErrorResponse |
| 404 | No visible organization has that id or slug, or the department is not one of the organization's own. | ErrorResponse |
| 500 | The database could not be read. | ErrorResponse |
/api/scorm-courses Bearer tokenRegister an uploaded package as a course
Completes a package upload: validates the staged zip (it must contain an imsmanifest.xml whose launch resource exists in the archive), captures the launch path, SCORM version, manifest objectives and mastery score, and creates the course — owned by the organization, or by the owning department named within it, tagged with the subjects named, and open to trainees unless audience says trainers only. Requires scorm:write over that owner. An invalid package is rejected and its staged blob deleted.
| Field | Type | Required | Description |
|---|---|---|---|
| org | string | yes | Organization UUID id or slug the course belongs to. at least 1 character |
| department | string | no | UUID id or slug of the owning department within the organization; omit for a course owned by the organization directly. Ownership scopes administration and limits taking the course to the department's role holders. at least 1 character |
| pathname | string | yes | matches ^scorm-courses\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\/[^/]{1,100}$ |
| file_name | string | yes | 1–255 characters |
| name | string | no | Defaults to the title in the package manifest, then the file name. at most 200 characters |
| description | string | no | at most 5000 characters |
| audience | string | no | Defaults to trainees: open to everyone the course's ownership admits. one of "trainees" | "trainers" |
| subject_matter_ids | array of string (uuid) | no | Subjects of the course's organization to tag it with; completion records are filed under all of them. Omit for an untagged course. at most 50 items |
Responses
| Status | Description | Body |
|---|---|---|
| 201 | The registered course. | ScormCourse |
| 400 | The request body failed validation, no upload exists at the pathname, or the package is not a runnable SCORM module. | ErrorResponse |
| 401 | The access token is missing or invalid. | ErrorResponse |
| 403 | The caller does not hold scorm:write over the intended owner. | ErrorResponse |
| 404 | No visible organization has that id or slug, or the department or a subject matter is not the organization's own. | ErrorResponse |
| 409 | That uploaded package is already registered. | ErrorResponse |
| 500 | The database could not be reached, or the write failed. | ErrorResponse |
| 503 | File storage is not configured on this deployment. | ErrorResponse |
/api/scorm-courses/{course_id}/subjects Bearer tokenReplace a course's subject tags
Sets the complete set of subjects the course — and every completion record it reports from now on — is filed under. Records already written keep their tags. Subjects of the course's own organization only. Requires scorm:write over the course's owner.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| course_id | path | string (uuid) | yes | UUID id of the SCORM course. |
| Field | Type | Required | Description |
|---|---|---|---|
| subject_matter_ids | array of string (uuid) | yes | Replaces the course's subject-matter tags. at most 50 items |
Responses
| Status | Description | Body |
|---|---|---|
| 200 | The course's subjects after the change, alphabetically. | array of ScormCourseSubject |
| 400 | The request body failed validation. | ErrorResponse |
| 401 | The access token is missing or invalid. | ErrorResponse |
| 403 | The caller does not hold scorm:write over the course's owner. | ErrorResponse |
| 404 | No visible course has that id, or a subject is not the organization's own. | ErrorResponse |
| 500 | The database could not be reached, or the write failed. | ErrorResponse |
/api/scorm-courses/{course_id}/package-uploads Bearer tokenMint an upload ticket for a replacement package
Issues a presigned upload URL for the zip that will replace this course's package, exactly as POST /scorm-courses/uploads does for a new course: the bytes go straight to the private blob store under a server-chosen pathname. The staged upload becomes the course's package only through PUT /scorm-courses/{course_id}/package, which validates it first. Requires scorm:write over the course's owner.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| course_id | path | string (uuid) | yes | UUID id of the SCORM course. |
| Field | Type | Required | Description |
|---|---|---|---|
| file_name | string | yes | 1–255 characters |
| size_bytes | integer | yes | Declared package size; the upload token enforces the cap. maximum 314572800 |
Responses
| Status | Description | Body |
|---|---|---|
| 201 | The presigned upload ticket. | ScormPackageUploadTicket |
| 400 | The request body failed validation. | ErrorResponse |
| 401 | The access token is missing or invalid. | ErrorResponse |
| 403 | The caller does not hold scorm:write over the course's owner. | ErrorResponse |
| 404 | No visible course has that id. | ErrorResponse |
| 500 | The upload ticket could not be issued. | ErrorResponse |
| 503 | File storage is not configured on this deployment. | ErrorResponse |
/api/scorm-courses/{course_id}/package Bearer tokenReplace the course's package
Makes a staged upload the course's next package version: the zip is validated like a new registration (an imsmanifest.xml whose launch resource exists), and its launch path, SCORM version, objectives and mastery score become what fresh attempts run. The course keeps its id, name, subjects, audience and every attempt on record, so nothing that points at it — proof rules, pathways, QR codes — changes; an attempt in progress keeps running, and resumes on, the version it was launched with, and every earlier version's package stays stored for that and for the record. requires_recertification decides what the replacement means for learners: true means only completions of this version or later satisfy scorm_course_complete rules from now on, so everyone certified on the course must complete it again; false leaves every earlier completion standing, for a typo or a broken link. An invalid package is rejected and its staged blob deleted. Requires scorm:write over the course's owner.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| course_id | path | string (uuid) | yes | UUID id of the SCORM course. |
| Field | Type | Required | Description |
|---|---|---|---|
| pathname | string | yes | matches ^scorm-courses\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\/[^/]{1,100}$ |
| file_name | string | yes | 1–255 characters |
| requires_recertification | boolean | yes | True when the new content must be completed again by everyone — new compliance material, say: completions of attempts on earlier package versions stop satisfying scorm_course_complete rules, an attempt still running an earlier version included. False for a minor fix — a typo, a broken link — that leaves every earlier completion standing. Attempts in progress keep running the version they started on either way. |
Responses
| Status | Description | Body |
|---|---|---|
| 200 | The course, now serving the new version to fresh attempts. | ScormCourse |
| 400 | The request body failed validation, no upload exists at the pathname, or the package is not a runnable SCORM module. | ErrorResponse |
| 401 | The access token is missing or invalid. | ErrorResponse |
| 403 | The caller does not hold scorm:write over the course's owner. | ErrorResponse |
| 404 | No visible course has that id. | ErrorResponse |
| 409 | That uploaded package is already registered to a course. | ErrorResponse |
| 500 | The database could not be reached, or the write failed. | ErrorResponse |
| 503 | File storage is not configured on this deployment. | ErrorResponse |
/api/scorm-courses/{course_id}/package-versions Bearer tokenList a course's package versions
Lists every package the course has served, newest first: the registered upload as version 1, then one per replacement through PUT /scorm-courses/{course_id}/package. Each says what its manifest declared, whether it required recertification, and whether it is the version fresh attempts launch against; an attempt names its own version as package_version_number. Requires scorm:read over the course's owner.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| course_id | path | string (uuid) | yes | UUID id of the SCORM course. |
Responses
| Status | Description | Body |
|---|---|---|
| 200 | The course's package versions. | ScormPackageVersionList |
| 401 | The access token is missing or invalid. | ErrorResponse |
| 404 | No visible course has that id. | ErrorResponse |
| 500 | The database could not be read. | ErrorResponse |
/api/scorm-courses/{course_id}/permanent-deletion Bearer tokenPreview permanently deleting a SCORM course
Counts everything DELETE on this path would remove: every learner's attempts (in-progress ones included), the learning records completions reported into, the proof rules requiring completion of the course, and the package. Nothing is changed. Requires scorm:delete over the course's owner — a grant separate from scorm:write, which only archives.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| course_id | path | string (uuid) | yes | UUID id of the SCORM course. |
Responses
| Status | Description | Body |
|---|---|---|
| 200 | What the deletion would take with it. | DeletionImpact |
| 401 | The access token is missing or invalid. | ErrorResponse |
| 403 | The caller does not hold scorm:delete over the course's owner. | ErrorResponse |
| 404 | No visible course has that id. | ErrorResponse |
| 500 | The database could not be read. | ErrorResponse |
/api/scorm-courses/{course_id}/permanent-deletion Bearer tokenPermanently delete a SCORM course
Hard-deletes the course — unlike archiving, which only hides it from learners. Every attempt is deleted, the learning records completions reported into go with their score and pass/fail evidence (so whatever they counted toward certification requirements disappears), the scorm_course_complete rules pointing at the course are deleted too, and the package is removed from the file store. Irreversible. Preview the cost with GET first. Requires scorm:delete over the course's owner — a grant separate from scorm:write, which only archives.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| course_id | path | string (uuid) | yes | UUID id of the SCORM course. |
Responses
| Status | Description | Body |
|---|---|---|
| 204 | The course, its attempts, their records, its rules and the package are gone. | — |
| 401 | The access token is missing or invalid. | ErrorResponse |
| 403 | The caller does not hold scorm:delete over the course's owner. | ErrorResponse |
| 404 | No visible course has that id. | ErrorResponse |
| 500 | The database could not be reached, or the write failed. | ErrorResponse |
/api/scorm-courses/{course_id} Bearer tokenRead one SCORM course
Reads one course. Requires scorm:read over the course's owner — its owning department when it has one, its organization otherwise.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| course_id | path | string (uuid) | yes | UUID id of the SCORM course. |
Responses
| Status | Description | Body |
|---|---|---|
| 200 | The course. | ScormCourse |
| 401 | The access token is missing or invalid. | ErrorResponse |
| 404 | No visible course has that id. | ErrorResponse |
| 500 | The database could not be read. | ErrorResponse |
/api/scorm-courses/{course_id} Bearer tokenEdit or archive a course
Changes a course's name, description or audience (trainees, or trainers only), or archives/restores it. The package is replaced whole through PUT /scorm-courses/{course_id}/package, and the subject tags as a set through PUT /scorm-courses/{course_id}/subjects. Attempts are history, so there is no delete; archiving hides the course from learners. Requires scorm:write over the course's owner.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| course_id | path | string (uuid) | yes | UUID id of the SCORM course. |
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | no | 1–200 characters |
| description | string | null | no | at most 5000 characters |
| audience | string | no | Whom the course is for. trainees: everyone its ownership admits — every member for an organization-owned course, the owning department's role holders for a department-owned one — trainers included. trainers: only holders of scorm:read over the course's owner, for modules that teach how to run the training rather than the training itself; the course is absent from everyone else's list and launches. one of "trainees" | "trainers" |
| archived | boolean | no | True archives the course (hidden from learners); false restores it. |
Responses
| Status | Description | Body |
|---|---|---|
| 200 | The updated course. | ScormCourse |
| 400 | The request body failed validation. | ErrorResponse |
| 401 | The access token is missing or invalid. | ErrorResponse |
| 403 | The caller does not hold scorm:write over the organization. | ErrorResponse |
| 404 | No visible course has that id. | ErrorResponse |
| 500 | The database could not be reached, or the write failed. | ErrorResponse |