Academy Statistics
Programmatic access to the academy figures shown in the CMS — onboarding and course completion, journey progress, and quiz performance — plus per-user progress for HR / LMS integrations.
These endpoints share their queries with the in-app academy statistics, so the numbers match what managers see in the CMS. Unlike the CMS there is no signed-in user, so figures cover the whole customer by default; pass group_ids on the aggregate endpoints to narrow them.
Required scope: read:stats
Endpoints
/api/open/v1/stats/academy/onboardingOnboarding completion totals
/api/open/v1/stats/academy/coursesCompletion rates per published course
/api/open/v1/stats/academy/courses/{course_id}/usersEvery employee a published course counts, with their progress (paginated)
/api/open/v1/stats/academy/journeysCompletion totals per published journey
/api/open/v1/stats/academy/quizzes/{quiz_id}Per-question quiz performance summary
/api/open/v1/stats/academy/users/{user_id}/coursesA single user's course progress
/api/open/v1/stats/academy/users/{user_id}/onboardingA single user's onboarding status
Group filtering
The three aggregate endpoints (/onboarding, /courses, /journeys) accept an optional group_ids filter.
| Query param | Notes |
|---|---|
group_ids | Optional. Comma-separated group IDs (e.g. 12,40). Counts are narrowed to users in the given departments / professions / custom groups: a user must be in at least one of the given groups of each type, so a department and a profession together match only users in both. A value that is not a comma-separated list of numbers returns 422. IDs that do not exist are ignored, and when none of them exist the figures cover the whole customer. |
Kiosk users, and users marked as excluded from statistics, are always left out of academy figures.
/stats/academy/onboarding
Onboarding-flow completion totals across all non-kiosk, non-deactivated users (narrowed by group_ids), and how quickly people finish onboarding.
{
"data": {
"completed": 312,
"not_completed": 88,
"deadline_weeks": 4,
"within_deadline_pct": 76.4,
"avg_days_to_complete": 19.2,
"median_days_to_complete": 16
}
}| Field | Notes |
|---|---|
completed / not_completed | Users who have / have not completed onboarding. |
deadline_weeks | The company's onboarding deadline in weeks, or null when none is set. |
within_deadline_pct | Share of users who finished onboarding within the deadline, 0–100 with one decimal. It counts users who finished plus users who are past the deadline without finishing; users still inside their deadline are left out. null when no deadline is set or nobody can be measured yet. |
avg_days_to_complete | Average days from registration to completing onboarding, over users who finished, with one decimal. null when nobody has finished. |
median_days_to_complete | Median of the same days. null when nobody has finished. |
Days are counted from the day the user registered, or the day a preboarding journey unlocked their onboarding. Users who were invited but never registered, and users still in preboarding, are left out of the three speed figures. Whole numbers are returned without a decimal (16, not 16.0).
/stats/academy/courses
Completion counts for every published course. Each course is counted against the users who can access it (its targeted groups), narrowed by group_ids.
{
"data": [
{
"id": 12,
"title": "Food Safety Basics",
"completed": 210,
"not_completed": 90
}
]
}| Query param | Notes |
|---|---|
course_ids | Optional. Comma-separated course IDs (e.g. 12,15). Only these courses are returned. Draft courses stay out. |
per_department | Optional. 1 or true adds departments to each course: the course's completion in each department, so one call fills a department-by-course table. 0 or false leaves it out; any other value returns 422. |
Response with per_department=1:
{
"data": [
{
"id": 12,
"title": "Food Safety Basics",
"completed": 210,
"not_completed": 90,
"departments": [
{ "id": 41, "name": "Bar", "completed": 21, "not_completed": 9 },
{ "id": 40, "name": "Kitchen", "completed": 38, "not_completed": 4 }
]
}
]
}| Field | Notes |
|---|---|
departments | One entry per department, by name, counting the department's members who can access the course. Departments where nobody can access the course are left out. Someone in several departments counts in each. With group_ids, only the departments among them are listed, and professions or custom groups in group_ids narrow the department entries as they narrow the course's totals. |
/stats/academy/courses/{course_id}/users
The people a course's figures in /stats/academy/courses are taken from, a page at a time, with each person's progress in the course: every employee who can access the course, registered or not — no kiosk, bot, shadow or deactivated users, and no users excluded from statistics. Use it to see who has and has not completed a course. Sorted by user id. Takes page and per_page (default 25, max 50); group_ids and since are not supported. Returns 404 when the course does not exist or is not published.
| Field | Notes |
|---|---|
user_id | The user's id. Look the user up with GET /users/{id}. |
has_passed | true when the user has completed the course, has completed every chapter (passing its quiz where it has one), or is awaiting manual sign-off. |
completed_at | ISO-8601 time the user completed the course, or null. |
chapters_completed_count | Number of chapters the user has completed. |
awaits_manual_completion | true when the user has met the requirements but the course needs manual sign-off. |
All fields count from the user's latest completion reset of the course. The endpoint only reads: unlike /stats/academy/users/{user_id}/courses, it records nothing for a course awaiting manual sign-off.
{
"data": [
{
"user_id": 81,
"has_passed": true,
"completed_at": "2026-05-01T10:00:00+02:00",
"chapters_completed_count": 4,
"awaits_manual_completion": false
},
{
"user_id": 94,
"has_passed": false,
"completed_at": null,
"chapters_completed_count": 1,
"awaits_manual_completion": false
}
],
"links": { "first": "...", "last": "...", "prev": null, "next": null },
"meta": { "current_page": 1, "last_page": 1, "per_page": 25, "total": 2, "from": 1, "to": 2 }
}See Pagination for links and meta.
/stats/academy/journeys
Completion totals for every published journey, including the onboarding flow. For each employee's status on a journey — assigned, due, completed, overdue — see Journeys.
{
"data": [
{
"id": 3,
"title": "Onboarding",
"type": "onboarding",
"completed": 312,
"not_completed": 88
},
{
"id": 7,
"title": "Leadership Track",
"type": "custom",
"completed": 41,
"not_completed": 19
}
]
}/stats/academy/quizzes/{quiz_id}
Per-question performance for a single quiz, lowest pass_rate first. pass_rate is passed as a percentage of respondents, rounded to a whole number. Questions nobody has answered are left out, as are answers from users excluded from statistics. respondents counts distinct users; passed counts fully correct attempts, so a user who took the quiz more than once can be counted in it more than once. group_ids is not supported. Returns 404 if the quiz does not exist or has been deleted.
{
"data": [
{
"question_id": 45,
"body": "At what core temperature is poultry safe to serve?",
"respondents": 120,
"passed": 96,
"pass_rate": 80
}
]
}/stats/academy/users/{user_id}/courses
A single user's progress across the published courses assigned to them. Resolves active employees only — kiosk, bot, shadow and banned users return 404, as does an unknown id.
| Field | Notes |
|---|---|
course_id | The course's id. |
title | The course title. |
has_passed | true when the user has passed the course (or it is awaiting manual sign-off). |
completed_at | Completion timestamp, or null if not completed. |
chapters_completed_count | Number of chapters the user has completed (respects completion resets). |
awaits_manual_completion | true when the user has met the requirements but the course needs manual sign-off. |
{
"data": [
{
"course_id": 12,
"title": "Food Safety Basics",
"has_passed": true,
"completed_at": "2026-05-01T10:00:00+02:00",
"chapters_completed_count": 4,
"awaits_manual_completion": false
}
]
}/stats/academy/users/{user_id}/onboarding
A single user's onboarding status. Resolves active employees only — kiosk, bot, shadow and banned users return 404, as does an unknown id.
| Field | Notes |
|---|---|
onboarding_completed_at | ISO-8601 timestamp the user completed onboarding, or null if not completed. |
is_overdue | true when the user has not completed onboarding within the configured time limit. |
onboarding_days | Days left to complete onboarding (negative when overdue), or null if no onboarding time limit is configured, the user has not registered, or a preboarding journey still locks their onboarding. |
{
"data": {
"onboarding_completed_at": "2026-05-01T10:00:00+02:00",
"is_overdue": false,
"onboarding_days": 12
}
}