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 the read scope. One known gap: the User Activity report’s total_logins is always null for now — it needs a per-session login-event store that is not yet built (last_login_at is 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/completions

Returns 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

ParameterTypeRequiredDescription
pageintegerNoDefault: 1
per_pageintegerNoDefault: 50, max: 200
course_idUUIDNoFilter to a specific course
user_idUUIDNoFilter to a specific user
group_idUUIDNoFilter to users in a specific group
completed_afterISO 8601NoFilter completions after this timestamp
completed_beforeISO 8601NoFilter completions before this timestamp
pass_failstringNopass or fail
sort_bystringNocompleted_at, score. Default: completed_at
sort_orderstringNoasc 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/enrollments

Returns 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

ParameterTypeRequiredDescription
pageintegerNoDefault: 1
per_pageintegerNoDefault: 50, max: 200
course_idUUIDNoFilter to a specific course
group_idUUIDNoFilter to a specific group
statusstringNoactive, completed, dropped, expired, waitlisted, pending_approval
enrolled_afterISO 8601NoFilter enrollments created after this timestamp
enrolled_beforeISO 8601NoFilter enrollments created before this timestamp
sort_orderstringNoasc 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/compliance

Returns 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

ParameterTypeRequiredDescription
pageintegerNoDefault: 1
per_pageintegerNoDefault: 50, max: 200
group_idUUIDNoFilter to a specific group
course_idUUIDNoFilter to a specific required course
statusstringNocompliant, non_compliant, expiring_soon (within 30 days)
as_ofISO 8601NoCompliance 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/activity

Returns 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

ParameterTypeRequiredDescription
pageintegerNoDefault: 1
per_pageintegerNoDefault: 50, max: 200
group_idUUIDNoFilter to a specific group
active_afterISO 8601NoInclude users with activity after this date
active_beforeISO 8601NoInclude users with activity before this date
include_inactivebooleanNoInclude users with no activity in the period. Default: false
user_typestringNoFilter 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/performance

Returns 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

ParameterTypeRequiredDescription
pageintegerNoDefault: 1
per_pageintegerNoDefault: 50, max: 200
course_idUUIDNoFilter to a specific course
categorystringNoFilter by course category
period_startISO 8601NoStart of the reporting period
period_endISO 8601NoEnd 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/export

Queues 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

FieldTypeRequiredDescription
report_typestringYescompletions, enrollments, compliance, user_activity, course_performance
formatstringNocsv (default) or xlsx
filtersobjectNoSame filter parameters as the corresponding GET endpoint
notify_emailstringNoEmail 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

ParameterTypeRequiredDescription
job_idUUIDYesJob 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.