API Reference
URL Endpoints
SCORM
| Method | URL | Description |
|---|---|---|
POST |
/lms/scorm-api/{attempt_id}/ |
SCORM runtime API communication |
GET |
/lms/scorm-content/{path} |
Secure SCORM asset serving |
GET |
/lms/scorm-lesson/{lesson_id}/play/ |
SCORM player view |
POST |
/lms/enroll/{course_id}/ |
Enroll the current user in a course |
H5P
| Method | URL | Description |
|---|---|---|
POST |
/lms/h5p-xapi/{activity_id}/ |
Ingest an xAPI statement for an H5P activity |
GET, POST |
/lms/h5p-content-user-data/{activity_id}/ |
Load/save H5P runtime state for resume/progress |
GET |
/lms/h5p-content/{path} |
Secure H5P asset serving |
SCORM Content View Customization
wagtail_lms.views.ServeScormContentView can be subclassed to customize SCORM asset
delivery without duplicating path-traversal protection:
get_storage_path(normalized_content_path)— Build the full storage key from the normalized path. Override to change the base directory.get_cache_control(content_type)— Return aCache-Controlheader value, orNoneto omit the header.should_redirect(content_type, storage_path)— ReturnTrueto issue a redirect to the storage URL instead of proxying the file (useful for large media on S3).get_redirect_url(storage_path)— Return the redirect URL (defaults to the storage backend's URL for the given path).
wagtail_lms.views.ServeH5PContentView is a minimal subclass that overrides only
get_storage_path() to use WAGTAIL_LMS_H5P_CONTENT_PATH.
SCORM Runtime API Methods
The POST /lms/scorm-api/{attempt_id}/ endpoint accepts a JSON body with a method
field and optional params list.
| Method | Description |
|---|---|
Initialize() |
Start a SCORM session |
Terminate() |
End a SCORM session |
GetValue(element) |
Retrieve a CMI data model value |
SetValue(element, value) |
Store a CMI data model value |
Commit() |
Flush buffered data to the server |
GetLastError() |
Return the last error code |
GetErrorString(code) |
Return a human-readable error description |
GetDiagnostic(code) |
Return diagnostic information for an error code |
H5P xAPI Endpoint
POST /lms/h5p-xapi/{activity_id}/
- Auth: Login required; CSRF protected.
- Body: JSON — any valid xAPI statement. The
verb.idfield drives attempt updates.
Verb → attempt field mapping:
verb.id |
Effect |
|---|---|
.../completed |
completion_status = "completed" |
.../passed |
success_status = "passed", completion_status = "completed" |
.../failed |
success_status = "failed" |
.../scored |
Score fields updated from result.score |
Scores are read from result.score: raw, min, max, scaled.
An H5PAttempt is created automatically on the first statement for a given
user/activity pair. Subsequent statements for the same pair update the existing attempt
and append a new H5PXAPIStatement record.
Response: 200 OK with {"status": "ok"} on success, 400 for invalid JSON,
405 for non-POST requests.
H5P Content User Data Endpoint
GET|POST /lms/h5p-content-user-data/{activity_id}/?dataType=<type>&subContentId=<id>
- Auth: Login required.
- CSRF: Exempt (H5P runtime submits form-encoded AJAX internally).
GET behavior
- Returns saved value for
(user, activity_id, dataType, subContentId). - Response shape:
{"success": true, "data": "<string>"}. - If no value exists:
{"success": true, "data": false}.
POST behavior
- Form field
datastores the value. - Special case:
data=0clears the stored value for that key. - Response shape:
{"success": true}.
This endpoint powers H5P resume/progress state and is wired via
h5p-lesson.js (options.ajax.contentUserDataUrl + options.saveFreq).
Models
SCORM
| Model | Purpose |
|---|---|
SCORMPackage |
Uploaded SCORM ZIP; auto-extracted on save |
SCORMAttempt |
Per-user, per-package learning attempt |
SCORMData |
Key-value CMI data store for an attempt |
H5P
| Model | Purpose |
|---|---|
H5PActivity |
Uploaded .h5p package (Wagtail snippet); auto-extracted on save |
H5PAttempt |
Per-user, per-activity progress (completion, success, scores) |
H5PXAPIStatement |
Raw xAPI statement log for an attempt |
H5PContentUserData |
Per-user/activity runtime state used by H5P resume/progress |
Course
| Model | Purpose |
|---|---|
CoursePage |
Wagtail Page representing a course; parent of lesson pages |
H5PLessonPage |
Child of CoursePage; StreamField body of rich text + H5P activities |
SCORMLessonPage |
Child of CoursePage; delivers a single SCORM package in an iframe player |
CourseEnrollment |
Tracks a user's enrollment in a course |
H5PLessonCompletion |
Records when a user completes all H5P activities in an H5PLessonPage |
Downstream Extensibility Settings
| Setting | Default | Purpose |
|---|---|---|
WAGTAIL_LMS_SCORM_PACKAGE_VIEWSET_CLASS |
"wagtail_lms.viewsets.SCORMPackageViewSet" |
Replace the SCORM package Wagtail admin viewset |
WAGTAIL_LMS_H5P_ACTIVITY_VIEWSET_CLASS |
"wagtail_lms.viewsets.H5PActivityViewSet" |
Replace the H5P activity Wagtail admin viewset |
WAGTAIL_LMS_H5P_SNIPPET_VIEWSET_CLASS |
"wagtail_lms.viewsets.H5PActivitySnippetViewSet" |
Replace the H5P snippet viewset used for chooser/admin snippet URLs |
WAGTAIL_LMS_CHECK_LESSON_ACCESS |
"wagtail_lms.access.default_lesson_access_check" |
Dotted-path callable used by H5PLessonPage.serve() |
WAGTAIL_LMS_REGISTER_DJANGO_ADMIN |
True |
Enable/disable wagtail-lms Django admin registration |
WAGTAIL_LMS_SCORM_ADMIN_CLASS |
"wagtail_lms.admin.SCORMPackageAdmin" |
Dotted-path Django ModelAdmin for SCORMPackage |
WAGTAIL_LMS_H5P_ADMIN_CLASS |
"wagtail_lms.admin.H5PActivityAdmin" |
Dotted-path Django ModelAdmin for H5PActivity |
WAGTAIL_LMS_CHECK_LESSON_ACCESS callable signature:
def check_access(request, lesson_page, course_page) -> bool:
...
Return True to allow access, False to redirect learners to the parent course page with an error message.
Subclassing CoursePage
If your project subclasses CoursePage, include both lesson page types in your
subclass's subpage_types so editors can add H5P and SCORM lessons:
class ExtendedCoursePage(CoursePage):
subpage_types = [
"wagtail_lms.H5PLessonPage",
"wagtail_lms.SCORMLessonPage",
]
# ... your extra fields
Both H5PLessonPage.parent_page_types and SCORMLessonPage.parent_page_types are
intentionally unrestricted (None), so no child-side patching is required.
If you previously set subpage_types = [] to prevent child pages entirely, update it
before using either lesson type. Wagtail will silently hide the "Add child page" option
for any type absent from subpage_types — editors will see no error, the option simply
won't appear.
Two Django system checks warn at startup when a CoursePage subclass has subpage_types
defined that omits one of the lesson types:
wagtail_lms.W001: ExtendedCoursePage subclasses CoursePage but its subpage_types does
not include 'wagtail_lms.H5PLessonPage'. H5P lessons cannot be added to this page type.
wagtail_lms.W002: ExtendedCoursePage subclasses CoursePage but its subpage_types does
not include 'wagtail_lms.SCORMLessonPage'. SCORM lessons cannot be added to this page type.