Skip to Content

Headless

Server-to-server endpoints for headless integrations: provision users, enroll learners and manage content programmatically. These endpoints require an lh_ API token.

Issue a user token

POST/api/v1/admin/{org_slug}/auth/token
API tokenrequired — no session fallback

Issue a JWT access token on behalf of a user. The user must belong to the organization matching the org_slug. Requires `users.action_read` permission.

Path parameters

org_slugstringrequired

Request bodyapplication/jsonrequired

user_idintegerrequired

ID of the user to issue a token for

Returns

TokenResponse — Access token minted on behalf of the target user.

access_tokenstringrequired

JWT access token

token_typestring

Token type (always 'bearer')

user_idintegerrequired

ID of the user the token was issued for

user_uuidstringrequired

UUID of the user

Error responses
  • 403 API token lacks permission, user not in org, or org_slug mismatch
  • 404 User or organization not found
  • 422 Validation ErrorHTTPValidationError
Request
Response
{
  "access_token": "string",
  "token_type": "bearer",
  "user_id": 1,
  "user_uuid": "string"
}

Check user course access

GET/api/v1/admin/{org_slug}/courses/{course_uuid}/access/{user_id}
API tokenrequired — no session fallback

Check if a user can access a specific course. Returns enrollment status and whether the course is public. Requires `courses.action_read` permission.

Path parameters

org_slugstringrequired
course_uuidstringrequired
user_idintegerrequired

Returns

CourseAccessResponse — Access check result — whether the user can view the course.

has_accessbooleanrequired

True if the user can access the course (public or enrolled)

is_enrolledbooleanrequired

True if the user is enrolled in the course

is_publicbooleanrequired

True if the course is public

is_publishedbooleanrequired

True if the course is published

Error responses
  • 404 Course not found
  • 422 Validation ErrorHTTPValidationError
Request
Response
{
  "has_access": false,
  "is_enrolled": false,
  "is_public": false,
  "is_published": false
}

Bulk unenroll users from a course

POST/api/v1/admin/{org_slug}/enrollments/bulk/unenroll
API tokenrequired — no session fallback

Mirror of bulk enroll. Users not currently enrolled are reported in `not_enrolled`.

Path parameters

org_slugstringrequired

Request bodyapplication/jsonrequired

course_uuidstringrequired
user_idsinteger[]required

Returns

BulkUnenrollResponse — Per-user unenroll summary: unenrolled, not_enrolled.

unenrolledinteger[]required
not_enrolledinteger[]required
Error responses
  • 404 Course not found
  • 422 Validation ErrorHTTPValidationError
Request
Response
{
  "unenrolled": [
    0
  ],
  "not_enrolled": [
    0
  ]
}

Enroll user in course

POST/api/v1/admin/{org_slug}/enrollments/{user_id}/{course_uuid}
API tokenrequired — no session fallback

Enroll a user in a course on their behalf. Creates the learning trail and trail run. The user must belong to the organization. Requires `courses.action_read` permission.

Path parameters

org_slugstringrequired
user_idintegerrequired
course_uuidstringrequired

Returns

TrailRead — User enrolled — returns the user's updated trail with all runs.

idinteger | null
trail_uuidstring | null
org_idintegerrequired
user_idintegerrequired
creation_datestring | null
update_datestring | null
runsTrailRunRead[]required
Show child attributes
idinteger | null
dataData
statusStatusEnum
STATUS_IN_PROGRESSSTATUS_COMPLETEDSTATUS_PAUSEDSTATUS_CANCELLED
trail_idinteger | null
course_idinteger | null
org_idinteger | null
user_idinteger | null
courseCourse | null
creation_datestring | null
update_datestring | null
course_total_stepsinteger
stepsTrailStep[]
Show child attributes
idinteger | null
completebooleanrequired
teacher_verifiedbooleanrequired
gradestringrequired
dataData
trailrun_idintegerrequired
trail_idintegerrequired
activity_idintegerrequired
course_idintegerrequired
org_idintegerrequired
user_idintegerrequired
creation_datestringrequired
update_datestringrequired
Error responses
  • 400 User is already enrolled
  • 404 User or course not found
  • 422 Validation ErrorHTTPValidationError
Request
Response
{
  "id": 1,
  "trail_uuid": "string",
  "org_id": 1,
  "user_id": 1,
  "creation_date": "string",
  "update_date": "string",
  "runs": [
    {
      "id": 1,
      "data": {},
      "status": "STATUS_IN_PROGRESS",
      "trail_id": 1,
      "course_id": 1,
      "org_id": 1,
      "user_id": 1,
      "course": {},
      "creation_date": "string",
      "update_date": "string",
      "course_total_steps": 0,
      "steps": [
        {
          "id": 1,
          "complete": false,
          "teacher_verified": false,
          "grade": "string",
          "data": {},
          "trailrun_id": 1,
          "trail_id": 1,
          "activity_id": 1,
          "course_id": 1,
          "org_id": 1,
          "user_id": 1,
          "creation_date": "string",
          "update_date": "string"
        }
      ]
    }
  ]
}

Unenroll user from course

DELETE/api/v1/admin/{org_slug}/enrollments/{user_id}/{course_uuid}
API tokenrequired — no session fallback

Unenroll a user from a course. Removes the enrollment and all associated progress (trail steps). Requires `courses.action_read` permission.

Path parameters

org_slugstringrequired
user_idintegerrequired
course_uuidstringrequired

Returns

UnenrollResponse — User unenrolled — trail steps for this course are deleted.

detailstringrequired
Error responses
  • 404 Enrollment not found
  • 422 Validation ErrorHTTPValidationError
Request
Response
{
  "detail": "string"
}

Get user enrollments

GET/api/v1/admin/{org_slug}/enrollments/{user_id}
API tokenrequired — no session fallback

Get all course enrollments for a user in the organization, including trail runs and steps. Requires `courses.action_read` permission.

Path parameters

org_slugstringrequired
user_idintegerrequired

Returns

TrailRead — The user's trail with every course enrollment and step.

idinteger | null
trail_uuidstring | null
org_idintegerrequired
user_idintegerrequired
creation_datestring | null
update_datestring | null
runsTrailRunRead[]required
Show child attributes
idinteger | null
dataData
statusStatusEnum
STATUS_IN_PROGRESSSTATUS_COMPLETEDSTATUS_PAUSEDSTATUS_CANCELLED
trail_idinteger | null
course_idinteger | null
org_idinteger | null
user_idinteger | null
courseCourse | null
creation_datestring | null
update_datestring | null
course_total_stepsinteger
stepsTrailStep[]
Show child attributes
idinteger | null
completebooleanrequired
teacher_verifiedbooleanrequired
gradestringrequired
dataData
trailrun_idintegerrequired
trail_idintegerrequired
activity_idintegerrequired
course_idintegerrequired
org_idintegerrequired
user_idintegerrequired
creation_datestringrequired
update_datestringrequired
Error responses
  • 404 User not found or not a member of this organization
  • 422 Validation ErrorHTTPValidationError
Request
Response
{
  "id": 1,
  "trail_uuid": "string",
  "org_id": 1,
  "user_id": 1,
  "creation_date": "string",
  "update_date": "string",
  "runs": [
    {
      "id": 1,
      "data": {},
      "status": "STATUS_IN_PROGRESS",
      "trail_id": 1,
      "course_id": 1,
      "org_id": 1,
      "user_id": 1,
      "course": {},
      "creation_date": "string",
      "update_date": "string",
      "course_total_steps": 0,
      "steps": [
        {
          "id": 1,
          "complete": false,
          "teacher_verified": false,
          "grade": "string",
          "data": {},
          "trailrun_id": 1,
          "trail_id": 1,
          "activity_id": 1,
          "course_id": 1,
          "org_id": 1,
          "user_id": 1,
          "creation_date": "string",
          "update_date": "string"
        }
      ]
    }
  ]
}

Get user progress in course

GET/api/v1/admin/{org_slug}/progress/{user_id}/{course_uuid}
API tokenrequired — no session fallback

Get a user's progress in a specific course including completion percentage and list of completed activity IDs. Requires `courses.action_read` permission.

Path parameters

org_slugstringrequired
user_idintegerrequired
course_uuidstringrequired

Returns

ProgressResponse — Progress summary (completion percentage + completed activity ids).

course_uuidstringrequired
user_idintegerrequired
total_activitiesintegerrequired

Total number of activities in the course

completed_activitiesintegerrequired

Number of activities the user has completed

completion_percentagenumberrequired

Completion percentage (0-100)

completed_activity_idsinteger[]required

IDs of completed activities

Error responses
  • 404 Course not found
  • 422 Validation ErrorHTTPValidationError
Request
Response
{
  "course_uuid": "string",
  "user_id": 1,
  "total_activities": 0,
  "completed_activities": 0,
  "completion_percentage": 0,
  "completed_activity_ids": [
    0
  ]
}

Mark activity as completed

POST/api/v1/admin/{org_slug}/progress/{user_id}/activities/{activity_uuid}/complete
API tokenrequired — no session fallback

Mark an activity as completed on behalf of a user. Automatically creates enrollment if needed. If this completes the entire course, a certificate is awarded (if configured). Requires `courses.action_read` permission.

Path parameters

org_slugstringrequired
user_idintegerrequired
activity_uuidstringrequired

Returns

ActivityCompletionResponse — Activity marked complete; includes whether this completed the course.

activity_uuidstringrequired
user_idintegerrequired
completedbooleanrequired
is_new_completionbooleanrequired

True if this was a new completion (not already completed)

course_completedbooleanrequired

True if this completion finished the entire course

Error responses
  • 404 Activity not found
  • 422 Validation ErrorHTTPValidationError
Request
Response
{
  "activity_uuid": "string",
  "user_id": 1,
  "completed": false,
  "is_new_completion": false,
  "course_completed": false
}

Undo activity completion

DELETE/api/v1/admin/{org_slug}/progress/{user_id}/activities/{activity_uuid}/complete
API tokenrequired — no session fallback

Remove a user's activity completion. Does not revoke certificates. Requires `courses.action_read` permission.

Path parameters

org_slugstringrequired
user_idintegerrequired
activity_uuidstringrequired

Returns

ActivityUncompletionResponse — Activity completion removed.

activity_uuidstringrequired
user_idintegerrequired
completedboolean
Error responses
  • 404 Activity not found
  • 422 Validation ErrorHTTPValidationError
Request
Response
{
  "activity_uuid": "string",
  "user_id": 1,
  "completed": false
}

Mark entire course as completed

POST/api/v1/admin/{org_slug}/progress/{user_id}/{course_uuid}/complete
API tokenrequired — no session fallback

Mark all activities in a course as completed for a user. Skips activities that are already completed. Awards a certificate if configured. Requires `courses.action_read` permission.

Path parameters

org_slugstringrequired
user_idintegerrequired
course_uuidstringrequired

Returns

CourseCompletionResponse — All activities marked complete; indicates whether a certificate was awarded.

course_uuidstringrequired
user_idintegerrequired
completed_countintegerrequired

Number of newly completed activities

already_completed_countintegerrequired

Number of activities that were already completed

total_activitiesintegerrequired
course_completedbooleanrequired
certificate_awardedbooleanrequired

True if a certificate was awarded as a result

Error responses
  • 404 Course not found
  • 422 Validation ErrorHTTPValidationError
Request
Response
{
  "course_uuid": "string",
  "user_id": 1,
  "completed_count": 0,
  "already_completed_count": 0,
  "total_activities": 0,
  "course_completed": false,
  "certificate_awarded": false
}

Get all user progress

GET/api/v1/admin/{org_slug}/progress/{user_id}
API tokenrequired — no session fallback

Get a progress summary across all courses a user is enrolled in. Returns completion percentage and status for each enrollment. Requires `courses.action_read` permission.

Path parameters

org_slugstringrequired
user_idintegerrequired

Returns

ProgressSummaryItem[] — One row per course enrollment with completion percentage and status.

course_uuidstringrequired
course_namestringrequired
statusstringrequired

Enrollment status (STATUS_IN_PROGRESS, STATUS_COMPLETED, etc.)

total_activitiesintegerrequired
completed_activitiesintegerrequired
completion_percentagenumberrequired
enrolled_atstringrequired
Error responses
  • 404 User not found or not a member of this organization
  • 422 Validation ErrorHTTPValidationError
Request
Response
[
  {
    "course_uuid": "string",
    "course_name": "Example name",
    "status": "string",
    "total_activities": 0,
    "completed_activities": 0,
    "completion_percentage": 0,
    "enrolled_at": "string"
  }
]

Get user trail with full progress breakdown

GET/api/v1/admin/{org_slug}/trails/{user_id}
API tokenrequired — no session fallback

Return the user's trail in the organization with a full chapter-by-chapter and activity-by-activity progress breakdown for every course they are enrolled in. Each activity carries its completion state, teacher verification flag, grade, and completion timestamp. Requires `courses.action_read` permission.

Path parameters

org_slugstringrequired
user_idintegerrequired

Returns

UserTrailDetail — Full trail breakdown grouped by course → chapter → activity.

trail_uuidstring | null

Null if the user has never started a trail in this org

user_idintegerrequired
org_idintegerrequired
creation_datestring | null
update_datestring | null
coursesTrailCourseProgress[]required
Show child attributes
course_uuidstringrequired
course_idintegerrequired
course_namestringrequired
statusstring | null

TrailRun status (STATUS_IN_PROGRESS, STATUS_COMPLETED, …); null when there is no enrollment row

enrolled_atstring | null

ISO timestamp the user was enrolled; null when there is no enrollment row

total_activitiesintegerrequired
completed_activitiesintegerrequired
completion_percentagenumberrequired
chaptersTrailChapterProgress[]required
Show child attributes
chapter_uuidstringrequired
chapter_idintegerrequired
namestringrequired
orderintegerrequired
total_activitiesintegerrequired
completed_activitiesintegerrequired
activitiesTrailActivityProgress[]required
Show child attributes
activity_uuidstringrequired
activity_idintegerrequired
namestringrequired
activity_typestringrequired
activity_sub_typestringrequired
orderintegerrequired

Order of this activity within its chapter

publishedbooleanrequired
completedbooleanrequired
teacher_verifiedboolean

Whether a teacher has verified the completion (assignments)

gradestring

Grade/feedback recorded on the trail step, when set

completed_atstring | null

ISO timestamp when the activity was marked complete; null if not completed

Error responses
  • 403 API token lacks access to this organization
  • 404 User not found or not a member of this organization
  • 422 Validation ErrorHTTPValidationError
Request
Response
{
  "trail_uuid": "string",
  "user_id": 1,
  "org_id": 1,
  "creation_date": "string",
  "update_date": "string",
  "courses": [
    {
      "course_uuid": "string",
      "course_id": 1,
      "course_name": "Example name",
      "status": "string",
      "enrolled_at": "string",
      "total_activities": 0,
      "completed_activities": 0,
      "completion_percentage": 0,
      "chapters": [
        {
          "chapter_uuid": "string",
          "chapter_id": 1,
          "name": "Example name",
          "order": 0,
          "total_activities": 0,
          "completed_activities": 0,
          "activities": [
            "…"
          ]
        }
      ]
    }
  ]
}

Get user trail breakdown for a single course

GET/api/v1/admin/{org_slug}/trails/{user_id}/courses/{course_uuid}
API tokenrequired — no session fallback

Return the user's full chapter-by-chapter and activity-by-activity progress breakdown for a single course. Surfaces progress even when the user has no explicit enrollment row (status will be null in that case). Requires `courses.action_read` permission.

Path parameters

org_slugstringrequired
user_idintegerrequired
course_uuidstringrequired

Returns

UserTrailDetail — Full breakdown for the requested course only.

trail_uuidstring | null

Null if the user has never started a trail in this org

user_idintegerrequired
org_idintegerrequired
creation_datestring | null
update_datestring | null
coursesTrailCourseProgress[]required
Show child attributes
course_uuidstringrequired
course_idintegerrequired
course_namestringrequired
statusstring | null

TrailRun status (STATUS_IN_PROGRESS, STATUS_COMPLETED, …); null when there is no enrollment row

enrolled_atstring | null

ISO timestamp the user was enrolled; null when there is no enrollment row

total_activitiesintegerrequired
completed_activitiesintegerrequired
completion_percentagenumberrequired
chaptersTrailChapterProgress[]required
Show child attributes
chapter_uuidstringrequired
chapter_idintegerrequired
namestringrequired
orderintegerrequired
total_activitiesintegerrequired
completed_activitiesintegerrequired
activitiesTrailActivityProgress[]required
Show child attributes
activity_uuidstringrequired
activity_idintegerrequired
namestringrequired
activity_typestringrequired
activity_sub_typestringrequired
orderintegerrequired

Order of this activity within its chapter

publishedbooleanrequired
completedbooleanrequired
teacher_verifiedboolean

Whether a teacher has verified the completion (assignments)

gradestring

Grade/feedback recorded on the trail step, when set

completed_atstring | null

ISO timestamp when the activity was marked complete; null if not completed

Error responses
  • 403 API token lacks access to this organization
  • 404 Course or user not found
  • 422 Validation ErrorHTTPValidationError
Request
Response
{
  "trail_uuid": "string",
  "user_id": 1,
  "org_id": 1,
  "creation_date": "string",
  "update_date": "string",
  "courses": [
    {
      "course_uuid": "string",
      "course_id": 1,
      "course_name": "Example name",
      "status": "string",
      "enrolled_at": "string",
      "total_activities": 0,
      "completed_activities": 0,
      "completion_percentage": 0,
      "chapters": [
        {
          "chapter_uuid": "string",
          "chapter_id": 1,
          "name": "Example name",
          "order": 0,
          "total_activities": 0,
          "completed_activities": 0,
          "activities": [
            "…"
          ]
        }
      ]
    }
  ]
}

Get user certificates

GET/api/v1/admin/{org_slug}/certifications/{user_id}
API tokenrequired — no session fallback

Get all certificates awarded to a user in the organization. Includes certification details and course information. Requires `certifications.action_read` permission.

Path parameters

org_slugstringrequired
user_idintegerrequired

Returns

CertificateItem[] — All certificates awarded to the user in this org.

certificate_userCertificate Userrequired
certificationCertificationrequired
courseCertificateCourseInforequired
Show child attributes
idinteger | null
course_uuidstringrequired
namestringrequired
descriptionstring | null
thumbnail_imagestring | null
Error responses
  • 404 User not found or not a member of this organization
  • 422 Validation ErrorHTTPValidationError
Request
Response
[
  {
    "certificate_user": {},
    "certification": {},
    "course": {
      "id": 1,
      "course_uuid": "string",
      "name": "Example name",
      "description": "An example description",
      "thumbnail_image": "string"
    }
  }
]

Provision a user

POST/api/v1/admin/{org_slug}/users
API tokenrequired — no session fallback

Create a user and attach them to the organization in one call. Designed for SSO/JIT provisioning — the user's email is auto-verified and the normal email-verification flow is skipped. If a user with the given email already exists in another organization, they are attached to this org (idempotent); the password/username/name fields in the request are ignored in that case.

Path parameters

org_slugstringrequired

Request bodyapplication/jsonrequired

emailstringrequired
usernamestringrequired
first_namestring
last_namestring
passwordstring | null

Optional — if omitted, treated as SSO user with empty password

role_idinteger

Role id for the org membership (default 4 = student)

extra_metadataExtra Metadata | null

Optional arbitrary JSON metadata attached to the user for headless integrations

Returns

UserRead — User created or attached to the org. Returns the user.

usernamestringrequired
first_namestringrequired
last_namestringrequired
emailstringrequired
avatar_imagestring | null
biostring | null
detailsDetails | null
profileProfile | null
extra_metadataExtra Metadata | null
idintegerrequired
user_uuidstringrequired
email_verifiedboolean
last_login_atstring | null
signup_methodstring | null
is_superadminboolean
Error responses
  • 400 Email already in this org, duplicate username, weak password, or invalid role_id
  • 403 Member limit reached, feature disabled, role belongs to another org, role is elevated (Admin/Maintainer), or token creator lacks privilege
  • 422 Validation ErrorHTTPValidationError
  • 429 Too many provisioning requests for this token
Request
Response
{
  "username": "string",
  "first_name": "Example name",
  "last_name": "Example name",
  "email": "ada@example.com",
  "avatar_image": "string",
  "bio": "string",
  "details": {},
  "profile": {},
  "extra_metadata": {},
  "id": 1,
  "user_uuid": "string",
  "email_verified": false,
  "last_login_at": "string",
  "signup_method": "string",
  "is_superadmin": false
}

Update a user's profile

PATCH/api/v1/admin/{org_slug}/users/{user_id}
API tokenrequired — no session fallback

Update profile fields of an org member. Supports partial updates — only fields present in the request body are changed. Duplicate email/username is rejected.

Path parameters

org_slugstringrequired
user_idintegerrequired

Request bodyapplication/jsonrequired

usernamestring | null
first_namestring | null
last_namestring | null
emailstring | null
avatar_imagestring | null
biostring | null
detailsDetails | null
profileProfile | null

Returns

UserRead — Updated user profile.

usernamestringrequired
first_namestringrequired
last_namestringrequired
emailstringrequired
avatar_imagestring | null
biostring | null
detailsDetails | null
profileProfile | null
extra_metadataExtra Metadata | null
idintegerrequired
user_uuidstringrequired
email_verifiedboolean
last_login_atstring | null
signup_methodstring | null
is_superadminboolean
Error responses
  • 400 Duplicate email or username
  • 404 User not in org
  • 422 Validation ErrorHTTPValidationError
Request
Response
{
  "username": "string",
  "first_name": "Example name",
  "last_name": "Example name",
  "email": "ada@example.com",
  "avatar_image": "string",
  "bio": "string",
  "details": {},
  "profile": {},
  "extra_metadata": {},
  "id": 1,
  "user_uuid": "string",
  "email_verified": false,
  "last_login_at": "string",
  "signup_method": "string",
  "is_superadmin": false
}

Remove a user from the organization

DELETE/api/v1/admin/{org_slug}/users/{user_id}
API tokenrequired — no session fallback

Remove a user's organization membership. The user account itself is preserved (it may belong to other orgs). Blocks removing the last admin.

Path parameters

org_slugstringrequired
user_idintegerrequired

Returns

RemoveUserResponse — Membership removed. The user account is preserved.

detailstringrequired
Error responses
  • 400 User is the last admin
  • 404 User not in org
  • 422 Validation ErrorHTTPValidationError
Request
Response
{
  "detail": "string"
}

Look up a user by email

GET/api/v1/admin/{org_slug}/users/by-email/{email}
API tokenrequired — no session fallback

Find a user by email within the organization. Returns 404 if the user does not exist or is not a member of this org.

Path parameters

org_slugstringrequired
emailstringrequired

URL-encoded email address

Returns

UserRead — The user matching the given email within this org.

usernamestringrequired
first_namestringrequired
last_namestringrequired
emailstringrequired
avatar_imagestring | null
biostring | null
detailsDetails | null
profileProfile | null
extra_metadataExtra Metadata | null
idintegerrequired
user_uuidstringrequired
email_verifiedboolean
last_login_atstring | null
signup_methodstring | null
is_superadminboolean
Error responses
  • 404 User not found in this organization
  • 422 Validation ErrorHTTPValidationError
Request
Response
{
  "username": "string",
  "first_name": "Example name",
  "last_name": "Example name",
  "email": "ada@example.com",
  "avatar_image": "string",
  "bio": "string",
  "details": {},
  "profile": {},
  "extra_metadata": {},
  "id": 1,
  "user_uuid": "string",
  "email_verified": false,
  "last_login_at": "string",
  "signup_method": "string",
  "is_superadmin": false
}

Issue a magic sign-in link

POST/api/v1/admin/{org_slug}/auth/magic-link
API tokenrequired — no session fallback

Generate a short-lived URL that, when opened in a browser, will log the target user in and optionally redirect them to a specific path. Token lifetime is clamped to 60–900 seconds.

Path parameters

org_slugstringrequired

Request bodyapplication/jsonrequired

user_idintegerrequired
redirect_tostring | null

Path to redirect to after the user lands on the consume endpoint

ttl_secondsinteger

Token lifetime in seconds (60–900)

Returns

MagicLinkResponse — Magic sign-in URL and underlying JWT. Token lifetime is already clamped.

urlstringrequired

Clickable URL to deliver to the user

tokenstringrequired

Raw JWT (for integrations that prefer to build their own URL)

expires_atstringrequired

ISO8601 timestamp when the token expires

Error responses
  • 400 redirect_to is not a same-origin path
  • 403 User not in this org
  • 404 User not found
  • 422 Validation ErrorHTTPValidationError
Request
Response
{
  "url": "https://example.com",
  "token": "string",
  "expires_at": "string"
}

Consume a magic sign-in link (browser-facing)

GET/api/v1/admin/{org_slug}/auth/magic-consume
API tokenrequired — no session fallback

Public endpoint — no API token required. Validates the magic-link JWT from the query string, sets authentication cookies, and redirects to the target path. On error, renders a friendly HTML page with a support link instead of a raw JSON error.

Path parameters

org_slugstringrequired

Query parameters

tokenstringrequired

Magic-link JWT

Error responses
  • 302 Success — cookies set and redirect to target
  • 410 Token invalid, expired, or user no longer a member (HTML page)
  • 422 Validation ErrorHTTPValidationError
Request
Response
{
  "detail": [
    {
      "loc": [
        "string"
      ],
      "msg": "string",
      "type": "string",
      "input": null,
      "ctx": {}
    }
  ]
}

Bulk enroll users in a course

POST/api/v1/admin/{org_slug}/enrollments/bulk
API tokenrequired — no session fallback

Enroll a batch of users into a single course. Users who are already enrolled are reported in `already_enrolled`. Users who are not members of the org are reported in `skipped`.

Path parameters

org_slugstringrequired

Request bodyapplication/jsonrequired

course_uuidstringrequired
user_idsinteger[]required

List of user ids to enroll (max 500)

Returns

BulkEnrollResponse — Per-user enrollment summary: enrolled, already_enrolled, skipped.

enrolledinteger[]required

User ids that were newly enrolled

already_enrolledinteger[]required

User ids that already had an enrollment

skippedinteger[]required

User ids that were not members of the org

Error responses
  • 404 Course not found
  • 422 Validation ErrorHTTPValidationError
Request
Response
{
  "enrolled": [
    0
  ],
  "already_enrolled": [
    0
  ],
  "skipped": [
    0
  ]
}

List users enrolled in a course

GET/api/v1/admin/{org_slug}/courses/{course_uuid}/enrollments
API tokenrequired — no session fallback

Reverse lookup: get all users currently enrolled in a course, with pagination. Returns enrollment status and enrolled_at per user.

Path parameters

org_slugstringrequired
course_uuidstringrequired

Query parameters

pageinteger
limitinteger

Returns

CourseEnrollmentItem[] — One row per enrolled user in this course, with status and enrolled_at.

userUserrequired
enrolled_atstringrequired
statusstringrequired
Error responses
  • 404 Course not found
  • 422 Validation ErrorHTTPValidationError
Request
Response
[
  {
    "user": {},
    "enrolled_at": "string",
    "status": "string"
  }
]

Reset a user's progress in a course

POST/api/v1/admin/{org_slug}/progress/{user_id}/{course_uuid}/reset
API tokenrequired — no session fallback

Delete all of a user's trail steps for a course, keeping the enrollment intact. The user can retake the course from scratch.

Path parameters

org_slugstringrequired
user_idintegerrequired
course_uuidstringrequired

Returns

ResetProgressResponse — Returns the number of trail steps deleted for this user/course.

course_uuidstringrequired
user_idintegerrequired
steps_deletedintegerrequired
Error responses
  • 404 User or course not found
  • 422 Validation ErrorHTTPValidationError
Request
Response
{
  "course_uuid": "string",
  "user_id": 1,
  "steps_deleted": 0
}

Manually award a certificate

POST/api/v1/admin/{org_slug}/certifications/{user_id}/{course_uuid}/award
API tokenrequired — no session fallback

Award a certificate to a user bypassing the normal completion gate. Useful for migrations and manual overrides. The course must have a certification configured.

Path parameters

org_slugstringrequired
user_idintegerrequired
course_uuidstringrequired

Returns

AwardCertificateResponse — Certificate awarded; returns the new user_certification_uuid.

user_certification_uuidstringrequired
user_idintegerrequired
course_uuidstringrequired
Error responses
  • 400 User already has a certificate for this course
  • 404 Course or certification not found
  • 422 Validation ErrorHTTPValidationError
Request
Response
{
  "user_certification_uuid": "string",
  "user_id": 1,
  "course_uuid": "string"
}

Revoke a user's certificate

DELETE/api/v1/admin/{org_slug}/certifications/{user_id}/{user_certification_uuid}
API tokenrequired — no session fallback

Delete a certificate row. Does not affect course enrollment or progress.

Path parameters

org_slugstringrequired
user_idintegerrequired
user_certification_uuidstringrequired

Returns

RevokeCertificateResponse — Certificate revoked. The cert row is hard-deleted.

detailstringrequired
user_certification_uuidstringrequired
Error responses
  • 404 Certificate not found
  • 422 Validation ErrorHTTPValidationError
Request
Response
{
  "detail": "string",
  "user_certification_uuid": "string"
}

Add a user to a user group

POST/api/v1/admin/{org_slug}/usergroups/{usergroup_uuid}/members/{user_id}
API tokenrequired — no session fallback

Add a user to a cohort/group. Both must belong to the token's org.

Path parameters

org_slugstringrequired
usergroup_uuidstringrequired
user_idintegerrequired

Returns

UserGroupMemberResponse — User added to the cohort.

detailstringrequired
usergroup_uuidstringrequired
user_idintegerrequired
Error responses
  • 400 User is already in this group
  • 404 User or group not found
  • 422 Validation ErrorHTTPValidationError
Request
Response
{
  "detail": "string",
  "usergroup_uuid": "string",
  "user_id": 1
}

Remove a user from a user group

DELETE/api/v1/admin/{org_slug}/usergroups/{usergroup_uuid}/members/{user_id}
API tokenrequired — no session fallback

Remove a user's membership from a cohort/group. The user account itself is preserved.

Path parameters

org_slugstringrequired
usergroup_uuidstringrequired
user_idintegerrequired

Returns

UserGroupMemberResponse — User removed from the cohort.

detailstringrequired
usergroup_uuidstringrequired
user_idintegerrequired
Error responses
  • 404 User, group, or membership not found
  • 422 Validation ErrorHTTPValidationError
Request
Response
{
  "detail": "string",
  "usergroup_uuid": "string",
  "user_id": 1
}

Change a user's org role

PATCH/api/v1/admin/{org_slug}/users/{user_id}/role
API tokenrequired — no session fallback

Change the user's role within the organization. Blocks demoting the last admin. The target role must belong to this org or be a global role.

Path parameters

org_slugstringrequired
user_idintegerrequired

Request bodyapplication/jsonrequired

role_idintegerrequired

Target role id

Returns

ChangeRoleResponse — User role updated.

user_idintegerrequired
role_idintegerrequired
Error responses
  • 400 Cannot demote the last admin
  • 403 Role does not belong to this organization
  • 404 User or role not found
  • 422 Validation ErrorHTTPValidationError
Request
Response
{
  "user_id": 1,
  "role_id": 1
}

Create a user group

POST/api/v1/admin/{org_slug}/usergroups
API tokenrequired — no session fallback

Create a cohort/user group owned by the organization.

Path parameters

org_slugstringrequired

Request bodyapplication/jsonrequired

namestringrequired
descriptionstring

Returns

UserGroupResponse — The newly created cohort.

idintegerrequired
org_idintegerrequired
usergroup_uuidstringrequired
namestringrequired
descriptionstringrequired
creation_datestringrequired
update_datestringrequired
Error responses
  • 422 Validation ErrorHTTPValidationError
Request
Response
{
  "id": 1,
  "org_id": 1,
  "usergroup_uuid": "string",
  "name": "Example name",
  "description": "An example description",
  "creation_date": "string",
  "update_date": "string"
}

Delete a user group

DELETE/api/v1/admin/{org_slug}/usergroups/{usergroup_uuid}
API tokenrequired — no session fallback

Delete a user group and all its memberships and course links. The underlying users and courses are untouched.

Path parameters

org_slugstringrequired
usergroup_uuidstringrequired

Returns

DeleteUserGroupResponse — Cohort and all its memberships/course links deleted.

detailstringrequired
usergroup_uuidstringrequired
Error responses
  • 404 UserGroup not found
  • 422 Validation ErrorHTTPValidationError
Request
Response
{
  "detail": "string",
  "usergroup_uuid": "string"
}

List members of a user group

GET/api/v1/admin/{org_slug}/usergroups/{usergroup_uuid}/members
API tokenrequired — no session fallback

Reverse lookup: list users belonging to a cohort, with pagination.

Path parameters

org_slugstringrequired
usergroup_uuidstringrequired

Query parameters

pageinteger
limitinteger

Returns

UserGroupMemberListItem[] — Members of the cohort, paginated.

userUserrequired
added_atstringrequired
Error responses
  • 404 UserGroup not found
  • 422 Validation ErrorHTTPValidationError
Request
Response
[
  {
    "user": {},
    "added_at": "string"
  }
]

List user groups a user belongs to

GET/api/v1/admin/{org_slug}/users/{user_id}/groups
API tokenrequired — no session fallback

Reverse lookup: which cohorts is this user a member of?

Path parameters

org_slugstringrequired
user_idintegerrequired

Returns

UserUserGroupItem[] — Cohorts the user belongs to within this org.

usergroupUsergrouprequired
added_atstringrequired
Error responses
  • 404 User not in org
  • 422 Validation ErrorHTTPValidationError
Request
Response
[
  {
    "usergroup": {},
    "added_at": "string"
  }
]

Grant a cohort access to a course

POST/api/v1/admin/{org_slug}/usergroups/{usergroup_uuid}/courses/{course_uuid}
API tokenrequired — no session fallback

Link a course to a user group — all members of the group gain the access rights configured for that group.

Path parameters

org_slugstringrequired
usergroup_uuidstringrequired
course_uuidstringrequired

Returns

UserGroupCourseResponse — Course linked to the cohort.

detailstringrequired
usergroup_uuidstringrequired
course_uuidstringrequired
Error responses
  • 400 Course already linked to this group
  • 404 Group or course not found
  • 422 Validation ErrorHTTPValidationError
Request
Response
{
  "detail": "string",
  "usergroup_uuid": "string",
  "course_uuid": "string"
}

Revoke a cohort's access to a course

DELETE/api/v1/admin/{org_slug}/usergroups/{usergroup_uuid}/courses/{course_uuid}
API tokenrequired — no session fallback

Unlink a course from a user group. Members of the cohort lose access unless granted through another path.

Path parameters

org_slugstringrequired
usergroup_uuidstringrequired
course_uuidstringrequired

Returns

UserGroupCourseResponse — Course unlinked from the cohort.

detailstringrequired
usergroup_uuidstringrequired
course_uuidstringrequired
Error responses
  • 404 Group or link not found
  • 422 Validation ErrorHTTPValidationError
Request
Response
{
  "detail": "string",
  "usergroup_uuid": "string",
  "course_uuid": "string"
}

Full GDPR data export for a user

GET/api/v1/admin/{org_slug}/users/{user_id}/export
API tokenrequired — no session fallback

Return a JSON bundle containing the user's profile, org memberships, trails, runs, steps, certificates, and cohort memberships. Intended for GDPR Article 15 (Right of Access) compliance. All sub-collections are scoped to the caller's org.

Path parameters

org_slugstringrequired
user_idintegerrequired

Returns

UserDataExportResponse — Bundle of all user data scoped to this org.

profileProfilerequired

The user's profile (scrubbed of sensitive fields)

membershipsobject[]required

Org memberships scoped to the caller's org

trailsobject[]required

Learning trails in the caller's org

trail_runsobject[]required

Course enrollments / trail runs

trail_stepsobject[]required

Activity completions

certificatesobject[]required

Certificates awarded in the caller's org

user_groupsobject[]required

Cohort memberships in the caller's org

exported_atstringrequired

ISO8601 timestamp when the export was generated

Error responses
  • 404 User not in org
  • 422 Validation ErrorHTTPValidationError
Request
Response
{
  "profile": {},
  "memberships": [
    {}
  ],
  "trails": [
    {}
  ],
  "trail_runs": [
    {}
  ],
  "trail_steps": [
    {}
  ],
  "certificates": [
    {}
  ],
  "user_groups": [
    {}
  ],
  "exported_at": "string"
}

GDPR right-to-be-forgotten

POST/api/v1/admin/{org_slug}/users/{user_id}/anonymize
API tokenrequired — no session fallback

Scrub a user's PII (email, name, avatar, bio, details, profile, password). Delete API tokens the user created. Keeps trails and certificates so course analytics remain accurate. Invalidates session cache.

Path parameters

org_slugstringrequired
user_idintegerrequired

Returns

AnonymizeUserResponse — User PII scrubbed; reports how many API tokens were revoked.

detailstringrequired
user_idintegerrequired
anonymized_emailstringrequired
api_tokens_revokedintegerrequired
Error responses
  • 404 User not in org
  • 422 Validation ErrorHTTPValidationError
Request
Response
{
  "detail": "string",
  "user_id": 1,
  "anonymized_email": "ada@example.com",
  "api_tokens_revoked": 0
}

Aggregate stats for a course

GET/api/v1/admin/{org_slug}/courses/{course_uuid}/analytics
API tokenrequired — no session fallback

Returns enrollment/completion counts, average completion percentage across all enrollees, and total certificates awarded.

Path parameters

org_slugstringrequired
course_uuidstringrequired

Returns

CourseAnalyticsResponse — Aggregate course stats: enrollment, completion, certificates, etc.

course_uuidstringrequired
enrollment_countintegerrequired
completed_countintegerrequired
in_progress_countintegerrequired
total_activitiesintegerrequired
average_completion_percentagenumberrequired
certificate_countintegerrequired
Error responses
  • 404 Course not found
  • 422 Validation ErrorHTTPValidationError
Request
Response
{
  "course_uuid": "string",
  "enrollment_count": 0,
  "completed_count": 0,
  "in_progress_count": 0,
  "total_activities": 0,
  "average_completion_percentage": 0,
  "certificate_count": 0
}