eXeLearning REST API Documentation¶
This document describes the REST APIs available in eXeLearning.
API Versions Overview¶
| Version | Base Path | Purpose | Status |
|---|---|---|---|
| API v1 | /api/v1 |
External integrations (LMS, mobile apps, automation) | Current |
| Legacy API | /api |
Internal use, compatibility | Deprecated |
API v1 — External Integration API¶
Base URL: /api/v1
Auth: Authorization: Bearer <JWT>
Real-time: Changes propagate to WebSocket clients via Yjs CRDTs
Purpose¶
API v1 is designed for external integrations: - Learning Management Systems (LMS) - Mobile applications - Automation scripts - Third-party tools
All changes made via the REST API are automatically synchronized with connected WebSocket clients using Yjs CRDTs for conflict resolution.
Authentication¶
Important: API v1 is only accessible to registered users (ROLE_USER or ROLE_ADMIN). Guest users are not allowed and will receive a 403 FORBIDDEN error.
Get a JWT Token¶
Option A — via CLI (development/automation)¶
bun run cli generate-jwt user@example.com --ttl=3600
Option B — via Login API¶
curl -X POST http://localhost:8080/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"email":"user@example.com","password":"secret"}'
# → { "access_token": "<JWT>", "token_type": "bearer" }
Using the Token¶
export TOKEN='<JWT>'
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8080/api/v1/projects
Response Format¶
All API v1 endpoints return consistent JSON responses:
Success Response¶
{
"success": true,
"data": { ... }
}
Error Response¶
{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "Project not found"
}
}
Common HTTP Status Codes¶
| Code | Meaning |
|---|---|
| 200 | Success |
| 201 | Created |
| 400 | Bad request / Validation error |
| 401 | Unauthorized (missing/invalid token) |
| 403 | Forbidden (no permission, or guest user attempting access) |
| 404 | Not found |
| 422 | Unprocessable entity (schema validation) |
| 500 | Internal server error |
Projects¶
List Projects¶
GET /api/v1/projects
Returns projects owned by the authenticated user (or all projects for admins).
Query Parameters:
- limit (number): Max results (default: 50)
- offset (number): Pagination offset
- search (string): Search in title
Response:
{
"success": true,
"data": [
{
"id": 1,
"uuid": "abc-123-def",
"title": "My Course",
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-16T14:20:00Z"
}
]
}
Get Project¶
GET /api/v1/projects/:uuid
Create Project¶
POST /api/v1/projects
Content-Type: application/json
{
"title": "New Course"
}
Update Project¶
PUT /api/v1/projects/:uuid
Content-Type: application/json
{
"title": "Updated Title"
}
Delete Project¶
DELETE /api/v1/projects/:uuid
Duplicate Project¶
POST /api/v1/projects/:uuid/duplicate
Content-Type: application/json
{
"title": "Copy of My Course"
}
Pages¶
List Pages¶
GET /api/v1/projects/:uuid/pages
Returns all pages in the project as a flat array, sorted by order.
Get Page¶
GET /api/v1/projects/:uuid/pages/:pageId
Create Page¶
POST /api/v1/projects/:uuid/pages
Content-Type: application/json
{
"name": "Introduction",
"parentId": null,
"order": 0
}
Parameters:
- name (required): Page name
- parentId (optional): Parent page ID for nested pages (null for root level)
- order (optional): Position in the page list
Update Page¶
PATCH /api/v1/projects/:uuid/pages/:pageId
Content-Type: application/json
{
"name": "Updated Name",
"properties": {
"customKey": "customValue"
}
}
Delete Page¶
DELETE /api/v1/projects/:uuid/pages/:pageId
Deletes the page and all its descendants.
Move Page¶
POST /api/v1/projects/:uuid/pages/:pageId/move
Content-Type: application/json
{
"parentId": "target-page-id",
"position": 0
}
Blocks¶
Blocks are containers within pages that hold components (iDevices).
List Blocks¶
GET /api/v1/projects/:uuid/pages/:pageId/blocks
Get Block¶
GET /api/v1/projects/:uuid/blocks/:blockId
Create Block¶
POST /api/v1/projects/:uuid/pages/:pageId/blocks
Content-Type: application/json
{
"name": "Content Section",
"order": 0
}
Update Block¶
PATCH /api/v1/projects/:uuid/blocks/:blockId
Content-Type: application/json
{
"name": "Updated Name",
"iconName": "icon-text",
"properties": {
"visibility": "true",
"teacherOnly": "false"
}
}
Delete Block¶
DELETE /api/v1/projects/:uuid/blocks/:blockId
Move Block¶
POST /api/v1/projects/:uuid/blocks/:blockId/move
Content-Type: application/json
{
"targetPageId": "page-xyz",
"position": 0
}
Components (iDevices)¶
Components are the content elements within blocks.
List Components¶
GET /api/v1/projects/:uuid/blocks/:blockId/components
Get Component¶
GET /api/v1/projects/:uuid/components/:componentId
Create Component¶
POST /api/v1/projects/:uuid/blocks/:blockId/components
Content-Type: application/json
{
"ideviceType": "text",
"initialData": {
"title": "My Text",
"htmlContent": "<p>Hello world</p>"
},
"order": 0
}
Parameters:
- ideviceType (required): Type of iDevice (text, quiz, image, etc.)
- initialData (optional): Initial content and properties
- order (optional): Position within the block
Update Component¶
PUT /api/v1/projects/:uuid/components/:componentId
Content-Type: application/json
{
"title": "Updated Title",
"htmlContent": "<p>Updated content</p>",
"properties": {
"customProp": "value"
}
}
Set Component HTML¶
Convenience endpoint to update only the HTML content:
PUT /api/v1/projects/:uuid/components/:componentId/html
Content-Type: application/json
{
"html": "<p>New HTML content</p>"
}
Delete Component¶
DELETE /api/v1/projects/:uuid/components/:componentId
Metadata¶
Get Project Metadata¶
GET /api/v1/projects/:uuid/metadata
Returns project metadata (title, author, description, language, etc.).
Update Project Metadata¶
PATCH /api/v1/projects/:uuid/metadata
Content-Type: application/json
{
"title": "Course Title",
"author": "John Doe",
"description": "Course description",
"language": "en"
}
Export¶
Export Project¶
GET /api/v1/projects/:uuid/export/:format
Formats:
- html5 - HTML5 website
- html5-sp - HTML5 single page
- scorm12 - SCORM 1.2 package
- scorm2004 - SCORM 2004 package
- ims - IMS Content Package
- epub3 - EPUB3 ebook
- elp / elpx - eXeLearning project file
Response: ZIP file download
List Available Formats¶
GET /api/v1/export/formats
Assets¶
Manage project assets (images, media files, documents).
Note: Changes are automatically broadcast to connected WebSocket clients via Yjs.
List Assets¶
GET /api/v1/projects/:uuid/assets
Returns all assets in the project.
Response:
{
"success": true,
"data": [
{
"id": 1,
"clientId": "abc-123-uuid",
"filename": "image.jpg",
"mimeType": "image/jpeg",
"size": 102400,
"folderPath": "images",
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-16T14:20:00Z"
}
]
}
Upload Asset¶
POST /api/v1/projects/:uuid/assets
Content-Type: multipart/form-data
file: <binary>
clientId: (optional) UUID for the asset
folderPath: (optional) Virtual folder path
Response (201 Created):
{
"success": true,
"data": {
"id": 1,
"clientId": "generated-uuid",
"filename": "image.jpg",
"mimeType": "image/jpeg",
"size": 102400,
"folderPath": "",
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T10:30:00Z"
}
}
Download Asset¶
GET /api/v1/projects/:uuid/assets/:assetId
Returns the asset file with appropriate Content-Type header.
Get Asset Metadata¶
GET /api/v1/projects/:uuid/assets/:assetId/metadata
Returns asset metadata without downloading the file.
Delete Asset¶
DELETE /api/v1/projects/:uuid/assets/:assetId
Deletes the asset from disk and database.
Bulk Delete Assets¶
POST /api/v1/projects/:uuid/assets/bulk-delete
Content-Type: application/json
{
"clientIds": ["uuid-1", "uuid-2", "uuid-3"]
}
Deletes multiple assets by their client IDs.
Response:
{
"success": true,
"data": {
"deleted": 3
}
}
Users (Admin Only)¶
These endpoints require ROLE_ADMIN.
List Users¶
GET /api/v1/users
Get User¶
GET /api/v1/users/:id
Create User¶
POST /api/v1/users
Content-Type: application/json
{
"email": "newuser@example.com",
"password": "securepassword",
"roles": ["ROLE_USER"]
}
Update User¶
PATCH /api/v1/users/:id
Content-Type: application/json
{
"roles": ["ROLE_USER", "ROLE_ADMIN"],
"is_active": true
}
Delete User¶
DELETE /api/v1/users/:id
Real-time Synchronization¶
All changes made via the REST API are automatically broadcast to connected WebSocket clients:
- REST request modifies the Y.Doc (Yjs document)
- Server calculates the delta update
- Delta is broadcast to all WebSocket clients in the project room
- Clients apply the update via Yjs CRDT merge
This ensures: - Consistency: All clients see the same state - Conflict resolution: Yjs CRDTs handle concurrent edits - Real-time updates: WebSocket clients update instantly
Examples¶
Create a Complete Page with Content¶
# 1. Create a page
PAGE=$(curl -s -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Chapter 1"}' \
http://localhost:8080/api/v1/projects/$UUID/pages)
PAGE_ID=$(echo $PAGE | jq -r '.data.id')
# 2. Create a block
BLOCK=$(curl -s -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Introduction"}' \
http://localhost:8080/api/v1/projects/$UUID/pages/$PAGE_ID/blocks)
BLOCK_ID=$(echo $BLOCK | jq -r '.data.id')
# 3. Add a text component
curl -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"ideviceType": "text",
"initialData": {
"title": "Welcome",
"htmlContent": "<p>Welcome to Chapter 1!</p>"
}
}' \
http://localhost:8080/api/v1/projects/$UUID/blocks/$BLOCK_ID/components
Export a Project to SCORM¶
curl -L -o course.zip \
-H "Authorization: Bearer $TOKEN" \
http://localhost:8080/api/v1/projects/$UUID/export/scorm12
Legacy API (Deprecated)¶
The legacy API at /api is maintained for backwards compatibility but should not be used for new integrations. See the sections below for reference only.
Legacy: ELP Conversion & Export Endpoints¶
These endpoints allow you to convert legacy ELP files to the current format and export ELP files to various output formats (HTML5, SCORM, EPUB, etc.).
Authentication: All endpoints require ROLE_USER and Authorization: Bearer <JWT>.
Request Format: multipart/form-data with file upload.
Convert ELP File¶
Endpoint: POST /api/convert/elp
Description: Converts old ELP files (contentv2/v3) to the current format (elpx/contentv4).
Request:
- Content-Type: multipart/form-data
- Body Parameters:
- file (required): The ELP file to convert
Query Parameters:
- download (optional): Set to 1 to download the converted file directly instead of returning JSON metadata.
Example:
# Convert and download directly
curl -X POST "http://localhost:8080/api/convert/elp?download=1" \
-H "Authorization: Bearer $TOKEN" \
-F "file=@/path/to/legacy.elp" \
-o converted.elpx
Export ELP File (Stateless)¶
Endpoints:
- POST /api/convert/export/html5 — Export to HTML5 web format
- POST /api/convert/export/scorm12 — Export to SCORM 1.2 format
- POST /api/convert/export/scorm2004 — Export to SCORM 2004 format
- POST /api/convert/export/epub3 — Export to EPUB3 format
- POST /api/convert/export/ims — Export to IMS Content Package format
Example:
# Export to SCORM 1.2 and download
curl -L -X POST "http://localhost:8080/api/convert/export/scorm12?download=1" \
-H "Authorization: Bearer $TOKEN" \
-F "file=@/path/to/course.elp" \
-o export_scorm12.zip
Configuration¶
Environment Variables¶
MAX_UPLOAD_SIZE: Maximum upload size (default: 100MB)FILES_DIR: Base directory for file storageJWT_SECRET: Secret for JWT token signing
Upload Size Limit¶
MAX_UPLOAD_SIZE=200M # or 200000000 for bytes