AllureLMS SCORM API Documentation
Complete documentation for the AllureLMS SCORM Player API - a production-ready REST API for managing SCORM 1.2/2004 packages, sessions, analytics, and xAPI telemetry.
Table of Contents
- Quick Start
- Authentication
- Rate Limiting
- Endpoints
- Error Handling
- Best Practices
- SDKs & Tools
- Additional Resources
Quick Start
Base URL
Production: https://connect.allurelms.com
Development: http://localhost:3001
API Version
Current: v1
All endpoints are prefixed with /api/v1/ except health checks.
Quick Example
// Upload a SCORM package
const formData = new FormData();
formData.append('file', scormZipFile);
formData.append('uploaded_by', 'user-123');
const response = await fetch('https://connect.allurelms.com/api/v1/packages', {
method: 'POST',
headers: {
'X-API-Key': 'your-api-key-here',
},
body: formData,
});
const { package_id, launch_url } = await response.json();
console.log(`Package uploaded: ${package_id}`);
console.log(`Launch URL: ${launch_url}`);
Authentication
All API requests (except /api/health) require authentication using API keys.
API Key Authentication
Include your API key in the X-API-Key header:
X-API-Key: scorm_abc123...
Example
curl -H "X-API-Key: scorm_abc123..." \
https://connect.allurelms.com/api/v1/packages
API Key Scopes
API keys have different permission scopes:
- read: List and view resources
- write: Create and update resources
- delete: Delete resources
Obtaining API Keys
Contact your administrator or use the admin portal to generate API keys.
Rate Limiting
Rate limits prevent abuse and ensure fair usage across all tenants.
Limits
- Read operations: 1000 requests per minute
- Write operations: 100 requests per minute
- Upload operations: 10 requests per minute
Rate Limit Headers
Responses include rate limit information:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 995
X-RateLimit-Reset: 1640995200
Exceeded Rate Limit
HTTP/1.1 429 Too Many Requests
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded"
}
}
Solution: Wait until the reset time or implement exponential backoff.
Endpoints
Health Check
GET /api/health
Check service health status.
Authentication: None required
Response:
{
"status": "healthy",
"service": "scorm-api",
"version": "1.5.0",
"timestamp": "2025-01-01T12:00:00Z",
"checks": {
"database": true
}
}
Packages
List Packages
GET /api/v1/packages
List all SCORM packages for the authenticated tenant.
Query Parameters:
page(integer, default: 1) - Page numberlimit(integer, default: 20, max: 100) - Results per page
Example:
curl -H "X-API-Key: scorm_abc123..." \
"https://connect.allurelms.com/api/v1/packages?page=1&limit=20"
Response:
{
"packages": [
{
"id": "pkg-123",
"tenant_id": "tenant-456",
"title": "Workplace Safety Essentials",
"version": "1.2",
"launch_url": "https://connect.allurelms.com/content/...",
"manifest_url": "https://connect.allurelms.com/content/.../imsmanifest.xml",
"file_size_bytes": 5242880,
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z"
}
],
"count": 42
}
Upload Package
POST /api/v1/packages
Upload a new SCORM package.
Content-Type: multipart/form-data
Form Fields:
file(required) - SCORM package ZIP file (max 10GB)uploaded_by(optional) - User ID
Example:
const formData = new FormData();
formData.append('file', scormZipFile);
formData.append('uploaded_by', 'user-123');
const response = await fetch('https://connect.allurelms.com/api/v1/packages', {
method: 'POST',
headers: { 'X-API-Key': apiKey },
body: formData,
});
Response:
{
"package_id": "pkg-123",
"title": "Course Title",
"version": "1.2",
"launch_url": "https://connect.allurelms.com/content/...",
"manifest_url": "https://connect.allurelms.com/content/.../imsmanifest.xml"
}
Get Package
GET /api/v1/packages/{id}
Get details of a specific package.
Response:
{
"id": "pkg-123",
"tenant_id": "tenant-456",
"title": "Course Title",
"version": "1.2",
"launch_url": "https://connect.allurelms.com/content/...",
"manifest_url": "https://connect.allurelms.com/content/.../imsmanifest.xml",
"file_size_bytes": 5242880,
"metadata": {
"description": "Course description",
"sco_count": 5
},
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z"
}
Delete Package
DELETE /api/v1/packages/{id}
Delete a SCORM package and all associated sessions.
Response:
{
"success": true,
"package_id": "pkg-123"
}
Sessions
List Sessions
GET /api/v1/sessions
List learner sessions with optional filtering.
Query Parameters:
package_id(UUID) - Filter by packageuser_id(UUID) - Filter by usercompletion_status(string) - Filter by completion (not_attempted, incomplete, completed)success_status(string) - Filter by success (unknown, passed, failed)page(integer, default: 1)limit(integer, default: 20, max: 100)
Example:
curl -H "X-API-Key: scorm_abc123..." \
"https://connect.allurelms.com/api/v1/sessions?package_id=pkg-123&completion_status=completed"
Response:
{
"sessions": [
{
"id": "session-789",
"package_id": "pkg-123",
"tenant_id": "tenant-456",
"user_id": "user-abc",
"completion_status": "completed",
"success_status": "passed",
"score": {
"scaled": 0.95,
"raw": 95,
"min": 0,
"max": 100
},
"attempts": 1,
"time_spent_seconds": 3600,
"session_time": "PT1H",
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T01:00:00Z",
"package": {
"title": "Course Title",
"version": "1.2"
}
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 150,
"total_pages": 8
}
}
Get Session
GET /api/v1/sessions/{id}
Get detailed session information including full CMI data.
Response:
{
"session": {
"id": "session-789",
"package_id": "pkg-123",
"user_id": "user-abc",
"completion_status": "completed",
"success_status": "passed",
"score": {
"scaled": 0.95,
"raw": 95,
"min": 0,
"max": 100
},
"cmi_data": {
"cmi.core.lesson_status": "completed",
"cmi.core.score.raw": "95",
"cmi.suspend_data": "..."
},
"package": {
"title": "Course Title"
}
}
}
Update Session
PUT /api/v1/sessions/{id}
Update session CMI data (learner progress).
Request Body:
{
"cmi_data": {
"cmi.core.lesson_status": "completed",
"cmi.core.score.raw": "95"
},
"completion_status": "completed",
"success_status": "passed",
"score": {
"scaled": 0.95,
"raw": 95,
"min": 0,
"max": 100
},
"session_time": "PT1H",
"suspend_data": "..."
}
Response:
{
"success": true,
"session_id": "session-789",
"updated_at": "2025-01-01T01:00:00Z"
}
Dispatches
Create Dispatch
POST /api/v1/dispatches
Create a dispatch for external SCORM package distribution.
Request Body:
{
"package_id": "pkg-123",
"dispatch_name": "Partner Training",
"registration_limit": 100,
"expires_in_hours": 720,
"allowed_domains": ["partner.com", "*.partner.com"]
}
Response:
{
"dispatch": {
"id": "dispatch-456",
"tenant_id": "tenant-789",
"package_id": "pkg-123",
"dispatch_name": "Partner Training",
"registration_limit": 100,
"registration_count": 0,
"expires_at": "2025-02-01T00:00:00Z",
"status": "active",
"token": "eyJhbGciOiJIUzI1NiIs...",
"launch_url": "https://connect.allurelms.com/player/dispatch/eyJhbGciOiJIUzI1NiIs...",
"created_at": "2025-01-01T00:00:00Z"
}
}
List Dispatches
GET /api/v1/dispatches
List all dispatches for the authenticated tenant.
Query Parameters:
page(integer, default: 1)limit(integer, default: 20, max: 100)
Response:
{
"dispatches": [
{
"id": "dispatch-456",
"package_id": "pkg-123",
"dispatch_name": "Partner Training",
"registration_limit": 100,
"registration_count": 25,
"expires_at": "2025-02-01T00:00:00Z",
"status": "active",
"created_at": "2025-01-01T00:00:00Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 5,
"total_pages": 1
}
}
Revoke Dispatch
DELETE /api/v1/dispatches/{id}
Revoke a dispatch and prevent further access.
Response:
{
"status": "revoked",
"dispatch_id": "dispatch-456"
}
Generate Dispatch ZIP
POST /api/v1/dispatches/{id}/generate
Trigger background generation of the offline dispatch ZIP file.
Response:
{
"success": true,
"message": "Dispatch generation queued.",
"dispatch_id": "dispatch-456"
}
xAPI (Experience API)
Store xAPI Statements
POST /api/v1/xapi/statements
Store one or more xAPI statements.
Request Body (Single):
{
"actor": {
"name": "John Doe",
"mbox": "mailto:john@example.com"
},
"verb": {
"id": "http://adlnet.gov/expapi/verbs/completed",
"display": { "en-US": "completed" }
},
"object": {
"id": "https://connect.allurelms.com/packages/pkg-123",
"definition": {
"name": { "en-US": "Course Title" },
"type": "http://adlnet.gov/expapi/activities/course"
}
},
"result": {
"score": { "scaled": 0.95, "raw": 95, "min": 0, "max": 100 },
"success": true,
"completion": true,
"duration": "PT1H30M"
},
"timestamp": "2025-01-01T12:00:00Z"
}
Request Body (Multiple):
[
{ /* statement 1 */ },
{ /* statement 2 */ }
]
Response (Single):
"550e8400-e29b-41d4-a716-446655440000"
Response (Multiple):
[
"550e8400-e29b-41d4-a716-446655440000",
"660f9511-f3ac-52e5-b827-557766551111"
]
Query xAPI Statements
GET /api/v1/xapi/statements
Query xAPI statements with filters.
Query Parameters:
statementId(UUID) - Get specific statementverb(URL) - Filter by verb IRIactivity(URL) - Filter by activity IRIregistration(UUID) - Filter by registrationsince(ISO 8601) - Statements stored sinceuntil(ISO 8601) - Statements stored untilpage(integer, default: 1)limit(integer, default: 20, max: 100)
Example:
curl -H "X-API-Key: scorm_abc123..." \
"https://connect.allurelms.com/api/v1/xapi/statements?verb=http://adlnet.gov/expapi/verbs/completed"
Response:
{
"statements": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"actor": { /* ... */ },
"verb": { /* ... */ },
"object": { /* ... */ },
"result": { /* ... */ },
"timestamp": "2025-01-01T12:00:00Z",
"stored": "2025-01-01T12:00:00.123Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 250,
"total_pages": 13
}
}
Get xAPI Statement
GET /api/v1/xapi/statements/{id}
Retrieve a specific xAPI statement.
Response:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"actor": { /* ... */ },
"verb": { /* ... */ },
"object": { /* ... */ },
"result": { /* ... */ },
"timestamp": "2025-01-01T12:00:00Z"
}
Actor Performance Analytics
GET /api/v1/xapi/analytics/actors
Get aggregated performance metrics by learner.
Query Parameters:
actor_mbox(email) - Filter by actor emailmin_activities(integer) - Minimum unique activitiessort(string) - Sort field (total_statements, avg_score_scaled, etc.)order(asc/desc, default: desc)page(integer, default: 1)limit(integer, default: 20, max: 100)
Response:
{
"actors": [
{
"actor_mbox": "john@example.com",
"actor_name": "John Doe",
"total_statements": 45,
"unique_activities": 12,
"completed_count": 10,
"passed_count": 9,
"failed_count": 1,
"avg_score_scaled": 0.87,
"first_activity": "2025-01-01T00:00:00Z",
"last_activity": "2025-01-15T14:30:00Z"
}
],
"pagination": { /* ... */ }
}
Activity Performance Analytics
GET /api/v1/xapi/analytics/activities
Get aggregated performance metrics by activity.
Query Parameters:
activity_id(URL) - Filter by activity IRIactivity_type(URL) - Filter by activity typepackage_id(UUID) - Filter by SCORM packagemin_learners(integer) - Minimum unique learnerssort(string) - Sort fieldorder(asc/desc, default: desc)page(integer, default: 1)limit(integer, default: 20, max: 100)
Response:
{
"activities": [
{
"activity_id": "https://connect.allurelms.com/packages/pkg-123",
"activity_type": "http://adlnet.gov/expapi/activities/course",
"package_id": "pkg-123",
"total_statements": 250,
"unique_learners": 50,
"completed_count": 45,
"passed_count": 42,
"failed_count": 3,
"avg_score_scaled": 0.82,
"first_activity": "2025-01-01T00:00:00Z",
"last_activity": "2025-01-15T16:00:00Z"
}
],
"pagination": { /* ... */ }
}
Error Handling
All errors follow a consistent format:
{
"error": {
"code": "ERROR_CODE",
"message": "Human-readable error message",
"details": {}
},
"request_id": "correlation-id"
}
Common Error Codes
| Status | Code | Description |
|---|---|---|
| 400 | BAD_REQUEST | Invalid request format |
| 400 | VALIDATION_ERROR | Failed validation |
| 401 | UNAUTHORIZED | Missing/invalid API key |
| 403 | FORBIDDEN | Insufficient permissions |
| 403 | QUOTA_EXCEEDED | Quota limit exceeded |
| 404 | NOT_FOUND | Resource not found |
| 404 | PACKAGE_NOT_FOUND | SCORM package not found |
| 404 | SESSION_NOT_FOUND | Session not found |
| 409 | CONFLICT | State conflict |
| 429 | RATE_LIMIT_EXCEEDED | Too many requests |
| 500 | INTERNAL_ERROR | Server error |
| 500 | DATABASE_ERROR | Database operation failed |
Error Response Examples
Validation Error:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": {
"fieldErrors": {
"email": ["Invalid email format"],
"age": ["Must be greater than 0"]
}
}
}
}
Quota Exceeded:
{
"error": {
"code": "QUOTA_EXCEEDED",
"message": "Package limit exceeded",
"details": {
"limit": 100,
"current": 100
}
}
}
Best Practices
1. Always Check Response Status
const response = await fetch(url, options);
if (!response.ok) {
const error = await response.json();
console.error(`API Error [${error.error.code}]: ${error.error.message}`);
throw new Error(error.error.message);
}
const data = await response.json();
2. Implement Retry Logic
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(url, options);
// Don't retry on client errors (4xx)
if (response.status >= 400 && response.status < 500) {
return response;
}
if (response.ok) {
return response;
}
// Retry on server errors (5xx)
if (i < maxRetries - 1) {
await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, i)));
}
} catch (error) {
if (i === maxRetries - 1) throw error;
}
}
}
3. Handle Rate Limiting
async function fetchWithRateLimit(url, options) {
const response = await fetch(url, options);
if (response.status === 429) {
const resetTime = response.headers.get('X-RateLimit-Reset');
const waitTime = (parseInt(resetTime!) - Date.now() / 1000) * 1000;
await new Promise(resolve => setTimeout(resolve, waitTime));
return fetchWithRateLimit(url, options); // Retry
}
return response;
}
4. Use Pagination
async function fetchAllPackages(apiKey) {
const allPackages = [];
let page = 1;
let hasMore = true;
while (hasMore) {
const response = await fetch(
`https://connect.allurelms.com/api/v1/packages?page=${page}&limit=100`,
{ headers: { 'X-API-Key': apiKey } }
);
const { packages, count } = await response.json();
allPackages.push(...packages);
hasMore = packages.length === 100;
page++;
}
return allPackages;
}
5. Monitor API Usage
Track your API usage to avoid hitting limits:
const response = await fetch(url, options);
const rateLimitRemaining = response.headers.get('X-RateLimit-Remaining');
const rateLimitReset = response.headers.get('X-RateLimit-Reset');
console.log(`Remaining requests: ${rateLimitRemaining}`);
console.log(`Reset time: ${new Date(parseInt(rateLimitReset!) * 1000)}`);
SDKs & Tools
Official SDKs
Coming Soon:
- Node.js SDK
- Python SDK
- PHP SDK
- .NET SDK
OpenAPI Specification
Download the OpenAPI 3.1 specification:
- YAML:
https://connect.allurelms.com/api/docs/openapi.yaml - JSON:
https://connect.allurelms.com/api/docs/openapi.json
Use with tools like:
- Postman
- Insomnia
- Swagger UI
- OpenAPI Generator
Postman Collection
Import our Postman collection for quick testing:
https://connect.allurelms.com/api/docs/postman-collection.json
Additional Resources
Documentation
- API Versioning Strategy - Version management and deprecation policy
- Code Quality Standards - Development standards and patterns
- xAPI Telemetry Guide - Complete xAPI implementation guide
- API Authentication - Authentication and security details
- Changelog - Version history and upgrade guides
Specifications
Support
- Documentation: https://docs.allurelms.com/api
- Email: engineering@allurelms.com
- Issues: https://github.com/AllureLMS/scorm-api/issues
- Status Page: https://status.allurelms.com
Community
- Discord: https://discord.gg/allurelms
- Twitter: @AllureLMS
- Blog: https://blog.allurelms.com
Last Updated: 2025-11-11 API Version: v1.5.0 OpenAPI Version: 2025-10-27