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

GET
/api/open/v1/stats/academy/onboarding

Onboarding completion totals

GET
/api/open/v1/stats/academy/courses

Completion rates per published course

GET
/api/open/v1/stats/academy/courses/{course_id}/users

Every employee a published course counts, with their progress (paginated)

GET
/api/open/v1/stats/academy/journeys

Completion totals per published journey

GET
/api/open/v1/stats/academy/quizzes/{quiz_id}

Per-question quiz performance summary

GET
/api/open/v1/stats/academy/users/{user_id}/courses

A single user's course progress

GET
/api/open/v1/stats/academy/users/{user_id}/onboarding

A single user's onboarding status

Group filtering

The three aggregate endpoints (/onboarding, /courses, /journeys) accept an optional group_ids filter.

Query paramNotes
group_idsOptional. 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
  }
}
FieldNotes
completed / not_completedUsers who have / have not completed onboarding.
deadline_weeksThe company's onboarding deadline in weeks, or null when none is set.
within_deadline_pctShare 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_completeAverage days from registration to completing onboarding, over users who finished, with one decimal. null when nobody has finished.
median_days_to_completeMedian 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 paramNotes
course_idsOptional. Comma-separated course IDs (e.g. 12,15). Only these courses are returned. Draft courses stay out.
per_departmentOptional. 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 }
      ]
    }
  ]
}
FieldNotes
departmentsOne 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.

FieldNotes
user_idThe user's id. Look the user up with GET /users/{id}.
has_passedtrue 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_atISO-8601 time the user completed the course, or null.
chapters_completed_countNumber of chapters the user has completed.
awaits_manual_completiontrue 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.

FieldNotes
course_idThe course's id.
titleThe course title.
has_passedtrue when the user has passed the course (or it is awaiting manual sign-off).
completed_atCompletion timestamp, or null if not completed.
chapters_completed_countNumber of chapters the user has completed (respects completion resets).
awaits_manual_completiontrue 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.

FieldNotes
onboarding_completed_atISO-8601 timestamp the user completed onboarding, or null if not completed.
is_overduetrue when the user has not completed onboarding within the configured time limit.
onboarding_daysDays 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
  }
}