Courses
Available. Course management shipped in
LMS-571: list/create/get/update/archive courses, list/add modules, and per-course enrollments. The Add Module endpoint covers content module types (video,audio,document,iframe,youtube_vimeo,quiz,html); SCORM/cmi5/AICC modules are added by uploading a package through the admin UI. Writes require thewritescope (archive requiresadmin).
The Courses API lets you manage your course catalog — create courses, maintain modules, and retrieve enrollment lists per course.
The Course Object
{
"id": "e7f6a5b4-8c9d-0123-efab-234567890123",
"title": "Workplace Safety Fundamentals",
"description": "An introduction to workplace health and safety regulations in Canada.",
"short_description": "Workplace health and safety basics for Canadian workplaces.",
"status": "published",
"category_id": "1a2b3c4d-5e6f-7890-abcd-456789012345",
"estimated_minutes": 45,
"current_version_id": "5e6f7a8b-9c0d-1234-efab-567890123456",
"published_at": "2026-01-12T15:00:00Z",
"created_at": "2026-01-10T09:00:00Z",
"updated_at": "2026-05-20T14:30:00Z",
"enrollment_count": 14
}Course status values:
| Status | Description |
|---|---|
draft | Not visible to learners |
published | Available for enrollment |
archived | Soft-deleted; hidden from catalog; history preserved |
Endpoints
List Courses
GET /api/v1/coursesReturns a paginated list of courses in the tenant’s catalog.
Authentication required: read scope
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
page | integer | No | Page number. Default: 1 |
per_page | integer | No | Default: 50, max: 200 |
status | string | No | Filter: draft, published, archived. Default: excludes archived |
category | string | No | Filter by category name (exact match) |
search | string | No | Case-insensitive search on title and description |
created_after | ISO 8601 | No | Filter courses created after this timestamp |
sort_by | string | No | created_at, title, updated_at. Default: created_at |
sort_order | string | No | asc or desc. Default: desc |
Example Request
curl "https://{your-subdomain}.embaylms.com/api/v1/courses?status=published&category=Compliance&per_page=20" \
-H "Authorization: Bearer ems_live_0000000000000000000000000000"Example Response
{
"data": [
{
"id": "e7f6a5b4-8c9d-0123-efab-234567890123",
"title": "Workplace Safety Fundamentals",
"short_description": "Workplace health and safety basics for Canadian workplaces.",
"status": "published",
"category_id": "1a2b3c4d-5e6f-7890-abcd-456789012345",
"estimated_minutes": 45,
"published_at": "2026-01-12T15:00:00Z",
"created_at": "2026-01-10T09:00:00Z",
"updated_at": "2026-05-20T14:30:00Z",
"enrollment_count": 14
}
],
"meta": {
"page": 1,
"per_page": 20,
"total": 87
},
"error": null
}Create Course
POST /api/v1/coursesCreates a new course in draft status.
Authentication required: write scope
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Yes | Course title |
description | string | No | Course description (plain text or Markdown) |
short_description | string | No | Short catalog blurb (max 500 characters) |
estimated_minutes | integer | No | Estimated completion time in minutes |
An initial draft version is created alongside the course, so modules can be added right away.
Example Request
curl -X POST https://{your-subdomain}.embaylms.com/api/v1/courses \
-H "Authorization: Bearer ems_live_0000000000000000000000000000" \
-H "Content-Type: application/json" \
-d '{
"title": "WHMIS 2015",
"description": "Workplace Hazardous Materials Information System refresher course.",
"short_description": "WHMIS refresher.",
"estimated_minutes": 30
}'Example Response
{
"data": {
"id": "2b3c4d5e-6f7a-8901-bcde-901234567890",
"title": "WHMIS 2015",
"description": "Workplace Hazardous Materials Information System refresher course.",
"short_description": "WHMIS refresher.",
"status": "draft",
"category_id": null,
"estimated_minutes": 30,
"created_at": "2026-06-05T10:15:00Z",
"updated_at": "2026-06-05T10:15:00Z"
},
"meta": null,
"error": null
}HTTP Status: 201 Created
Get Course
GET /api/v1/courses/{id}Returns a single course by UUID.
Authentication required: read scope
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | UUID | Yes | Course UUID |
Example Request
curl "https://{your-subdomain}.embaylms.com/api/v1/courses/e7f6a5b4-8c9d-0123-efab-234567890123" \
-H "Authorization: Bearer ems_live_0000000000000000000000000000"Example Response
{
"data": {
"id": "e7f6a5b4-8c9d-0123-efab-234567890123",
"title": "Workplace Safety Fundamentals",
"description": "An introduction to workplace health and safety regulations in Canada.",
"short_description": "Workplace health and safety basics for Canadian workplaces.",
"status": "published",
"category_id": "1a2b3c4d-5e6f-7890-abcd-456789012345",
"estimated_minutes": 45,
"current_version_id": "5e6f7a8b-9c0d-1234-efab-567890123456",
"published_at": "2026-01-12T15:00:00Z",
"created_at": "2026-01-10T09:00:00Z",
"updated_at": "2026-05-20T14:30:00Z",
"enrollment_count": 14
},
"meta": null,
"error": null
}Common Errors
| HTTP Status | Description |
|---|---|
404 | No course with this ID in the tenant |
Update Course
PATCH /api/v1/courses/{id}Updates one or more fields on a course. Supports publishing a draft (status: "published"), unpublishing back to draft, or archiving (status: "archived", equivalent to DELETE).
Authentication required: write scope
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | UUID | Yes | Course UUID |
Request Body
All fields are optional.
| Field | Type | Description |
|---|---|---|
title | string | Course title |
description | string | Course description |
short_description | string | Short catalog blurb (max 500 characters) |
status | string | draft, published, or archived. "archived" is equivalent to the DELETE endpoint below |
Example Request
curl -X PATCH "https://{your-subdomain}.embaylms.com/api/v1/courses/2b3c4d5e-6f7a-8901-bcde-901234567890" \
-H "Authorization: Bearer ems_live_0000000000000000000000000000" \
-H "Content-Type: application/json" \
-d '{ "status": "published" }'Example Response
Returns the updated course object (same structure as Get Course).
Archive Course
DELETE /api/v1/courses/{id}Archives a course. This is a soft delete — the course is hidden from the catalog and no new enrollments can be created, but existing learner history and completions are preserved.
Authentication required: admin scope
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | UUID | Yes | Course UUID |
Example Request
curl -X DELETE "https://{your-subdomain}.embaylms.com/api/v1/courses/e7f6a5b4-8c9d-0123-efab-234567890123" \
-H "Authorization: Bearer ems_live_0000000000000000000000000000"HTTP Status: 204 No Content
List Modules
GET /api/v1/courses/{id}/modulesReturns all modules for a course in display order.
Authentication required: read scope
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | UUID | Yes | Course UUID |
Example Request
curl "https://{your-subdomain}.embaylms.com/api/v1/courses/e7f6a5b4-8c9d-0123-efab-234567890123/modules" \
-H "Authorization: Bearer ems_live_0000000000000000000000000000"Example Response
{
"data": [
{
"id": "3c4d5e6f-7a8b-9012-cdef-012345678901",
"title": "Introduction to WHMIS",
"description": "Overview of hazard classes and labels.",
"module_type": "video",
"is_required": true,
"order_index": 0,
"created_at": "2026-01-10T09:00:00Z"
},
{
"id": "4d5e6f7a-8b9c-0123-defa-123456789012",
"title": "Final Assessment",
"description": null,
"module_type": "quiz",
"is_required": true,
"order_index": 1,
"created_at": "2026-01-10T09:05:00Z"
}
],
"meta": null,
"error": null
}Add Module
POST /api/v1/courses/{id}/modulesAdds a new module to a course. The module is appended at the end of the module list by default.
Authentication required: write scope
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | UUID | Yes | Course UUID |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Yes | Module title |
module_type | string | Yes | video, audio, document, iframe, youtube_vimeo, quiz, or html. SCORM/cmi5/AICC modules are added by uploading a package through the admin UI |
description | string | No | Module description (max 2000 characters) |
is_required | boolean | No | Whether completion is required for course completion. Default: true |
Example Request
curl -X POST "https://{your-subdomain}.embaylms.com/api/v1/courses/e7f6a5b4-8c9d-0123-efab-234567890123/modules" \
-H "Authorization: Bearer ems_live_0000000000000000000000000000" \
-H "Content-Type: application/json" \
-d '{
"title": "Video: Hazardous Material Handling",
"module_type": "video",
"is_required": true
}'Example Response
{
"data": {
"id": "5e6f7a8b-9c0d-1234-efab-234567890123",
"title": "Video: Hazardous Material Handling",
"description": null,
"module_type": "video",
"is_required": true,
"order_index": 2,
"created_at": "2026-06-05T10:20:00Z"
},
"meta": null,
"error": null
}HTTP Status: 201 Created
List Course Enrollments
GET /api/v1/courses/{id}/enrollmentsReturns all enrollments for a specific course.
Authentication required: read scope
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | UUID | Yes | Course UUID |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
status | string | No | Filter: active, completed, dropped, expired, waitlisted, pending_approval |
page | integer | No | Default: 1 |
per_page | integer | No | Default: 50, max: 200 |
Example Request
curl "https://{your-subdomain}.embaylms.com/api/v1/courses/e7f6a5b4-8c9d-0123-efab-234567890123/enrollments?status=active" \
-H "Authorization: Bearer ems_live_0000000000000000000000000000"Example Response
{
"data": [
{
"id": "d6e5f4a3-7b8c-9012-defa-123456789012",
"user_id": "a3f2c1d0-4e5b-6789-abcd-ef0123456789",
"course_id": "e7f6a5b4-8c9d-0123-efab-234567890123",
"status": "active",
"due_date": "2026-05-01T00:00:00Z",
"completed_at": null,
"source": "api",
"created_at": "2026-04-01T08:00:00Z"
}
],
"meta": { "page": 1, "per_page": 50, "total": 14 },
"error": null
}