cmi5 Integration Guide
This guide walks system administrators through integrating Creatiq H5P content with cmi5-compliant systems, enabling standardized content launch and xAPI-based learning analytics.
1. Overview
What is cmi5?
cmi5 is an xAPI profile that defines a standardized way to launch, track, and score e-learning content. It builds on top of the Experience API (xAPI) specification by adding:
- A defined launch mechanism with URL parameters
- A fetch endpoint for secure xAPI authentication
- Predefined xAPI verbs for lifecycle tracking (initialized, completed, passed, failed, etc.)
- A structured scoring model
How Creatiq Supports cmi5
Creatiq implements the cmi5 launch protocol for its H5P content library. This allows any cmi5-compliant LMS or content player to:
- Register a learner for a specific H5P content item.
- Launch the content in a browser with cmi5 parameters.
- Track the learner's session status (initialized, launched, completed, passed, failed, abandoned, waived, terminated).
- Record scores with the standard scaled/raw/min/max model.
- Send xAPI statements to Creatiq's xAPI endpoint using the cmi5 verb set.
cmi5 integration requires the Premium plan and the cmi5-launch feature flag.
2. Registration
Before a learner can launch content, you must register the content-learner combination.
Prerequisites
| Requirement | Detail |
|---|---|
| Creatiq plan | Premium or Enterprise |
| Feature flag | cmi5-launch enabled for your tenant |
| Content ID | A valid H5P content identifier in Creatiq |
| Actor account | An xAPI Agent account with homePage and name |
Register Content
POST /api/cmi5/register
Authorization: Bearer <api-token>
Content-Type: application/json
{
"contentId": "h5p-content-abc123",
"learnerId": "learner-456",
"actorAccount": {
"homePage": "https://lms.example.com",
"name": "learner-456"
}
}
Validation constraints:
| Field | Type | Constraints |
|---|---|---|
contentId | string | 1-500 characters |
learnerId | string | 1-255 characters |
actorAccount.homePage | string | Valid URL, max 500 characters |
actorAccount.name | string | 1-255 characters |
Response:
{
"success": true,
"data": {
"id": "reg-uuid-1234",
"contentId": "h5p-content-abc123",
"learnerId": "learner-456",
"actorAccount": {
"homePage": "https://lms.example.com",
"name": "learner-456"
},
"status": "initialized",
"sessionId": null,
"score": null,
"launchMode": "Normal",
"createdAt": "2026-03-25T10:00:00.000Z",
"updatedAt": "2026-03-25T10:00:00.000Z"
}
}
The registration is uniquely keyed on (contentId, learnerId). If the same combination is registered again, the existing record is returned with an updated timestamp (upsert behavior).
3. Launch Flow
Step 1: Generate a Launch URL
GET /api/cmi5/launch/<registrationId>
Authorization: Bearer <api-token>
Response:
{
"success": true,
"data": {
"launchUrl": "https://creatiq.example.com/h5p/play/h5p-content-abc123?endpoint=...&fetch=...®istration=...&activityId=...&actor=..."
}
}
Step 2: Open the Launch URL
Redirect the learner's browser to the launchUrl. This opens the H5P content player with the cmi5 parameters embedded in the query string.
What Happens Internally
When a launch URL is generated, the following occurs:
- The registration record is looked up by ID.
- A unique fetch token is generated (UUID, valid for 1 hour).
- A unique session ID is created and stored on the registration.
- The registration status is updated to
launched. - The launch URL is constructed with the following query parameters:
| Parameter | Value | Description |
|---|---|---|
endpoint | https://creatiq.example.com/api/xapi/statements | xAPI statement endpoint |
fetch | https://creatiq.example.com/api/cmi5/fetch/<token> | One-time fetch URL for auth |
registration | <registrationId> | The cmi5 registration UUID |
activityId | https://creatiq.example.com/h5p/content/<contentId> | Activity IRI |
actor | JSON-encoded xAPI Agent | Learner identity |
Step 3: Fetch Token Exchange
When the content (Assignable Unit) loads, it must call the fetch URL to obtain an xAPI authentication token:
POST /api/cmi5/fetch/<fetchToken>
This endpoint:
- Validates the fetch token (single-use, 1-hour expiry).
- Invalidates the fetch token so it cannot be reused.
- Returns a JWT
auth-tokenvalid for 1 hour.
Response:
{
"auth-token": "eyJhbGciOiJIUzI1NiIsInR5cCI6..."
}
The auth token contains:
{
"registrationId": "<uuid>",
"purpose": "cmi5-xapi-auth"
}
The content player uses this token as a Bearer token in the Authorization header when sending xAPI statements to the endpoint URL.
Complete Launch Sequence Diagram
LMS/App Creatiq API H5P Player xAPI Endpoint
| | | |
| 1. POST /api/cmi5/register | |
|-------------------------->| | |
| 2. Registration created | | |
|<--------------------------| | |
| | | |
| 3. GET /api/cmi5/launch/<id> | |
|-------------------------->| | |
| 4. Launch URL returned | | |
|<--------------------------| | |
| | | |
| 5. Redirect learner to launch URL | |
|-----------------------------------------------> | |
| | | |
| | 6. POST /api/cmi5/fetch/<token> |
| |<-----------------------| |
| | 7. Return auth-token | |
| |----------------------->| |
| | | |
| | | 8. POST xAPI statements
| | |--------------------->|
| | | |
4. Session Tracking
Session Statuses
Each cmi5 registration tracks the learner's progress through a defined set of statuses:
| Status | Description |
|---|---|
initialized | Registration created, content not yet launched |
launched | Content has been launched (launch URL generated) |
completed | Learner finished the content |
passed | Learner passed the content (score meets threshold) |
failed | Learner failed the content |
abandoned | Learner left without completing |
waived | Content was waived (skipped by admin/instructor) |
terminated | Session was explicitly terminated |
Get Session Status
GET /api/cmi5/sessions/<registrationId>
Authorization: Bearer <api-token>
Response:
{
"success": true,
"data": {
"id": "reg-uuid-1234",
"contentId": "h5p-content-abc123",
"learnerId": "learner-456",
"actorAccount": {
"homePage": "https://lms.example.com",
"name": "learner-456"
},
"status": "completed",
"sessionId": "session-uuid-789",
"score": {
"scaled": 0.85,
"raw": 85,
"min": 0,
"max": 100
},
"launchMode": "Normal",
"createdAt": "2026-03-25T10:00:00.000Z",
"updatedAt": "2026-03-25T10:30:00.000Z"
}
}
Update Session Status
For internal or service-to-service use, you can update a session's status and score:
PUT /api/cmi5/sessions/<registrationId>
Authorization: Bearer <api-token>
Content-Type: application/json
{
"status": "completed",
"score": {
"scaled": 0.85,
"raw": 85,
"min": 0,
"max": 100
}
}
Validation constraints:
| Field | Type | Constraints |
|---|---|---|
status | enum | One of: initialized, launched, completed, passed, failed, abandoned, waived, terminated |
score.scaled | number | Optional, 0.0 to 1.0 |
score.raw | number | Optional |
score.min | number | Optional |
score.max | number | Optional |
When a score is provided, it is merged with any existing score (existing fields are preserved if not overwritten).
Launch Modes
cmi5 defines three launch modes:
| Mode | Description |
|---|---|
Normal | Standard learning mode (default). Statements are recorded. |
Browse | Preview mode. The learner can explore content without recording results. |
Review | Review mode. The learner revisits completed content. |
5. xAPI Statements
Statement Endpoint
The cmi5 content player sends xAPI statements to:
POST /api/xapi/statements
Authorization: Bearer <cmi5-auth-token>
The auth-token obtained from the fetch endpoint is used as the Bearer token.
cmi5 Verbs
Creatiq recognizes and processes the following cmi5-defined xAPI verbs:
| Verb | IRI | Description |
|---|---|---|
| Initialized | http://adlnet.gov/expapi/verbs/initialized | Content was loaded and ready |
| Completed | http://adlnet.gov/expapi/verbs/completed | Learner completed the content |
| Passed | http://adlnet.gov/expapi/verbs/passed | Learner achieved a passing score |
| Failed | http://adlnet.gov/expapi/verbs/failed | Learner did not achieve a passing score |
| Abandoned | https://w3id.org/xapi/adl/verbs/abandoned | Learner left without completing |
| Waived | https://w3id.org/xapi/adl/verbs/waived | Content requirements were waived |
| Satisfied | https://w3id.org/xapi/adl/verbs/satisfied | Objective or block was satisfied |
| Terminated | http://adlnet.gov/expapi/verbs/terminated | Session was terminated |
Statement Context
All cmi5 xAPI statements should include the cmi5 context category in the context.contextActivities.category array:
{
"verb": {
"id": "http://adlnet.gov/expapi/verbs/completed",
"display": { "en-US": "completed" }
},
"actor": {
"objectType": "Agent",
"account": {
"homePage": "https://lms.example.com",
"name": "learner-456"
}
},
"object": {
"id": "https://creatiq.example.com/h5p/content/h5p-content-abc123",
"objectType": "Activity",
"definition": {
"type": "https://w3id.org/xapi/cmi5/activitytype/block"
}
},
"result": {
"score": {
"scaled": 0.85,
"raw": 85,
"min": 0,
"max": 100
},
"completion": true,
"success": true
},
"context": {
"registration": "reg-uuid-1234",
"contextActivities": {
"category": [
{
"id": "https://w3id.org/xapi/cmi5/context/categories/cmi5"
},
{
"id": "https://w3id.org/xapi/cmi5/context/categories/moveon"
}
]
}
}
}
cmi5 Context Constants
| Constant | Value |
|---|---|
| Activity Type | https://w3id.org/xapi/cmi5/activitytype/block |
| Context Category | https://w3id.org/xapi/cmi5/context/categories/cmi5 |
| MoveOn Category | https://w3id.org/xapi/cmi5/context/categories/moveon |
6. Configuration
Environment Variables
| Variable | Required | Description |
|---|---|---|
JWT_SECRET | Yes | Secret key for signing cmi5 auth tokens |
NEXT_PUBLIC_APP_URL | Yes | Public URL of the Creatiq application |
Feature Flags
| Flag | Required For |
|---|---|
cmi5-launch | Content registration, launch URL generation, and session status endpoints |
Endpoint Reference
| Method | Path | Auth | Description |
|---|---|---|---|
POST | /api/cmi5/register | Bearer token + cmi5-launch | Register content for a learner |
GET | /api/cmi5/launch/:registrationId | Bearer token + cmi5-launch | Generate a launch URL |
POST | /api/cmi5/fetch/:token | None (token in URL) | Exchange fetch token for xAPI auth token |
GET | /api/cmi5/sessions/:registrationId | Bearer token + cmi5-launch | Get session status |
PUT | /api/cmi5/sessions/:registrationId | Bearer token | Update session status and score |
Database
The cmi5 integration uses one database table (auto-created on startup):
| Table | Purpose |
|---|---|
cmi5_registrations | Stores content-learner registrations, session state, and scores |
Schema:
| Column | Type | Description |
|---|---|---|
id | UUID | Primary key (auto-generated) |
content_id | VARCHAR(500) | H5P content identifier |
learner_id | VARCHAR(255) | Learner identifier |
actor_account | JSONB | xAPI Agent account (homePage, name) |
status | VARCHAR(50) | Current session status (default: initialized) |
session_id | VARCHAR(255) | Session UUID (set on launch) |
score | JSONB | Score object (scaled, raw, min, max) |
launch_mode | VARCHAR(20) | Launch mode (default: Normal) |
created_at | TIMESTAMP | Record creation time |
updated_at | TIMESTAMP | Last update time |
Unique constraint: (content_id, learner_id) -- one registration per content-learner pair.
Troubleshooting
| Symptom | Cause | Solution |
|---|---|---|
| 403 on registration/launch | Feature flag cmi5-launch not enabled | Enable the feature for your tenant |
| "JWT_SECRET environment variable is required" | Missing secret | Set JWT_SECRET in your environment |
| "Invalid or expired fetch token" | Token was already used or expired | Generate a new launch URL (each fetch token is single-use, 1-hour expiry) |
| "Registration not found" | Invalid registration ID | Verify the registration was created and use the correct UUID |
| xAPI statements rejected (401) | Auth token expired or invalid | Re-launch the content to get a fresh fetch token and auth token |
| Duplicate registration returns existing record | Expected upsert behavior | The (contentId, learnerId) pair is unique; re-registering updates the timestamp |