Reports
Fully available (
LMS-572). All five reports — Completions, Enrollments, Compliance, User Activity, and Course Performance — and the async export job endpoints (POST /api/v1/reports/export,GET /api/v1/reports/export/{job_id}) are live. All report endpoints require thereadscope. One known gap: the User Activity report’stotal_loginsis alwaysnullfor now — it needs a per-session login-event store that is not yet built (last_login_atis the available login signal).
The Reports API provides access to aggregated learning data across your tenant. All report endpoints return paginated data. For large data sets, use the async export endpoints.
Endpoints
Completions Report
GET /api/v1/reports/completionsReturns a list of course completions with optional filters. Each row includes a language field — the content language the learner was enrolled in (their chosen language for a multi-language course, otherwise the course’s base language).
Authentication required: read scope
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
page | integer | No | Default: 1 |
per_page | integer | No | Default: 50, max: 200 |
course_id | UUID | No | Filter to a specific course |
user_id | UUID | No | Filter to a specific user |
group_id | UUID | No | Filter to users in a specific group |
completed_after | ISO 8601 | No | Filter completions after this timestamp |
completed_before | ISO 8601 | No | Filter completions before this timestamp |
pass_fail | string | No | pass or fail |
sort_by | string | No | completed_at, score. Default: completed_at |
sort_order | string | No | asc or desc. Default: desc |
Example Request
curl "https://{your-subdomain}.embaylms.com/api/v1/reports/completions?completed_after=2026-01-01T00:00:00Z" \
-H "Authorization: Bearer ems_live_0000000000000000000000000000"Example Response
{
"data": [
{
"enrollment_id": "d6e5f4a3-7b8c-9012-defa-123456789012",
"user_id": "a3f2c1d0-4e5b-6789-abcd-ef0123456789",
"course_id": "e7f6a5b4-8c9d-0123-efab-234567890123",
"course_title": "WHMIS 2015",
"completed_at": "2026-03-15T11:30:00Z",
"language": "en",
"score": 92,
"pass_fail": "pass",
"time_spent_seconds": 1820,
"certificate_id": "f8a7b6c5-9d0e-1234-fabc-345678901234"
}
],
"meta": { "page": 1, "per_page": 50, "total": 1480 },
"error": null
}Enrollments Report
GET /api/v1/reports/enrollmentsReturns enrollment activity over a time period. Each row includes a language field — the content language the learner was enrolled in (their chosen language for a multi-language course, otherwise the course’s base language).
Authentication required: read scope
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
page | integer | No | Default: 1 |
per_page | integer | No | Default: 50, max: 200 |
course_id | UUID | No | Filter to a specific course |
group_id | UUID | No | Filter to a specific group |
status | string | No | active, completed, dropped, expired, waitlisted, pending_approval |
enrolled_after | ISO 8601 | No | Filter enrollments created after this timestamp |
enrolled_before | ISO 8601 | No | Filter enrollments created before this timestamp |
sort_order | string | No | asc or desc by enrollment date. Default: desc |
Example Response
{
"data": [
{
"enrollment_id": "d6e5f4a3-7b8c-9012-defa-123456789012",
"user_id": "a3f2c1d0-4e5b-6789-abcd-ef0123456789",
"course_id": "e7f6a5b4-8c9d-0123-efab-234567890123",
"course_title": "Workplace Safety Fundamentals",
"status": "active",
"enrolled_at": "2026-04-01T08:00:00Z",
"due_date": "2026-05-01T00:00:00Z",
"completed_at": null,
"source": "native",
"language": "en"
}
],
"meta": { "page": 1, "per_page": 50, "total": 23 },
"error": null
}Compliance Report
GET /api/v1/reports/complianceReturns compliance status per user per assigned course (one row per assigned enrollment; dropped, waitlisted, and pending-approval enrollments are not assignments). A row is compliant when the enrollment has a current completion at as_of; expiring_soon when the completion expires within 30 days of as_of (recertification expiry); otherwise non_compliant.
Authentication required: read scope
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
page | integer | No | Default: 1 |
per_page | integer | No | Default: 50, max: 200 |
group_id | UUID | No | Filter to a specific group |
course_id | UUID | No | Filter to a specific required course |
status | string | No | compliant, non_compliant, expiring_soon (within 30 days) |
as_of | ISO 8601 | No | Compliance snapshot at a specific date. Default: now |
Example Response
{
"data": [
{
"user_id": "a3f2c1d0-4e5b-6789-abcd-ef0123456789",
"course_id": "e7f6a5b4-8c9d-0123-efab-234567890123",
"course_title": "WHMIS 2015",
"compliance_status": "non_compliant",
"last_completion_date": null,
"certificate_expires_at": null,
"days_until_expiry": null
}
],
"meta": { "page": 1, "per_page": 50, "total": 47 },
"error": null
}User Activity Report
GET /api/v1/reports/users/activityReturns user login and learning activity metrics over a time period (default: the last 30 days). A user is “active” when they logged in or started/completed an enrollment inside the period. total_logins is always null for now (see the note at the top of this page); total_time_spent_seconds sums course time for the user’s enrollments last active inside the period.
Authentication required: read scope
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
page | integer | No | Default: 1 |
per_page | integer | No | Default: 50, max: 200 |
group_id | UUID | No | Filter to a specific group |
active_after | ISO 8601 | No | Include users with activity after this date |
active_before | ISO 8601 | No | Include users with activity before this date |
include_inactive | boolean | No | Include users with no activity in the period. Default: false |
user_type | string | No | Filter by audience attribute: internal, partner, customer, contractor, or volunteer (LMS-780) |
Example Response
{
"data": [
{
"user_id": "a3f2c1d0-4e5b-6789-abcd-ef0123456789",
"last_login_at": "2026-06-04T14:22:11Z",
"total_logins": null,
"total_time_spent_seconds": 18200,
"courses_started": 3,
"courses_completed": 2,
"period_start": "2026-05-01T00:00:00Z",
"period_end": "2026-06-05T00:00:00Z"
}
],
"meta": { "page": 1, "per_page": 50, "total": 218 },
"error": null
}Course Performance Report
GET /api/v1/reports/courses/performanceReturns performance metrics per course (draft courses excluded) including completion rates, average scores, and average time to complete. period_start/period_end restrict which enrollments count (by enrollment creation date). average_score is the mean of each learner’s best passing quiz score; pass_rate_percent is measured over enrollments with at least one graded attempt (null when none); drop_off_rate_percent counts dropped + expired enrollments.
Authentication required: read scope
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
page | integer | No | Default: 1 |
per_page | integer | No | Default: 50, max: 200 |
course_id | UUID | No | Filter to a specific course |
category | string | No | Filter by course category |
period_start | ISO 8601 | No | Start of the reporting period |
period_end | ISO 8601 | No | End of the reporting period |
Example Response
{
"data": [
{
"course_id": "e7f6a5b4-8c9d-0123-efab-234567890123",
"course_title": "WHMIS 2015",
"total_enrollments": 312,
"completions": 287,
"completion_rate_percent": 92,
"average_score": 84.3,
"pass_rate_percent": 96,
"average_time_to_complete_seconds": 1950,
"drop_off_rate_percent": 8
}
],
"meta": { "page": 1, "per_page": 50, "total": 22 },
"error": null
}Queue Export Job
POST /api/v1/reports/exportQueues an asynchronous export job. Use this for data sets larger than 1,000 rows (exports are capped at 50,000 rows). Returns a job_id to poll for status. When notify_email is set, that address receives a heads-up email when the export is ready (the download itself always goes through the authenticated status endpoint below).
Authentication required: read scope
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
report_type | string | Yes | completions, enrollments, compliance, user_activity, course_performance |
format | string | No | csv (default) or xlsx |
filters | object | No | Same filter parameters as the corresponding GET endpoint |
notify_email | string | No | Email address to notify when the export is ready |
Example Response
{
"data": {
"job_id": "7a8b9c0d-1e2f-3456-bcde-456789012345",
"status": "queued",
"report_type": "compliance",
"format": "csv",
"created_at": "2026-06-05T11:00:00Z",
"estimated_completion_seconds": 30
},
"meta": null,
"error": null
}HTTP Status: 202 Accepted
Get Export Job Status
GET /api/v1/reports/export/{job_id}Polls an async export job. When complete, a pre-signed S3 download URL is returned valid for 15 minutes. A job id belonging to another tenant returns 404. progress_percent is coarse (0 queued, 50 processing, 100 completed); error is populated only on a failed job.
Authentication required: read scope
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
job_id | UUID | Yes | Job UUID returned by POST /reports/export |
Example Response — Completed
{
"data": {
"job_id": "7a8b9c0d-1e2f-3456-bcde-456789012345",
"status": "completed",
"report_type": "compliance",
"format": "csv",
"progress_percent": 100,
"row_count": 1482,
"file_size_bytes": 245760,
"error": null,
"created_at": "2026-06-05T11:00:00Z",
"completed_at": "2026-06-05T11:00:42Z",
"download_url": "https://exports.embaylms.com/tenant-acme/exports/compliance-2026-06-05.csv?X-Amz-Signature=abc123...",
"download_url_expires_at": "2026-06-05T11:15:42Z"
},
"meta": null,
"error": null
}Job status values: queued, processing, completed, failed
Poll every 5-10 seconds. If status is failed, create a new export job to retry.