CouchCertified All docs →

API reference

The HTTP API behind the CouchCertified platform. This page covers authentication, conventions, the team-management endpoints used by the team pages, and the public endpoints.

Heads-up: there are no standalone API keys today. Every authenticated call runs as you, using the same session your browser gets when you sign in. The API is stable enough to script against, but it is versionless and may evolve — pin your expectations to this page.

Authentication

Sign-in is password-less. To authenticate a script:

  1. Request a magic link: POST /api/auth/magic-link with {"email": "you@district.org"}. The link arrives by email.
  2. The link's code is single-use. Exchanging it (GET /api/auth/callback?code=…) yields a short-lived access token (JWT) and a refresh-cookie session.
  3. Send the token on every call: Authorization: Bearer <token>.

Browsers refresh the access token automatically from the session cookie; scripts should simply re-authenticate when they receive 401.

Conventions

Team management

All team endpoints require authentication and a role on that team. The minimum role is listed per endpoint — Viewer+ means Viewer, Manager, or Owner; Manager+ means Manager or Owner. Calls against a team you don't belong to return 404.

EndpointRoleWhat it does
GET /api/team/mineanyYour teams + your role in each. Returns {teams:[{group_id, uuid, name, role}]}.
GET /api/team/:groupIdViewer+Overview: team name, your role, and per-course seat utilization {purchased, assigned, reserved, remaining, completed}.
PATCH /api/team/:groupId
{name}
OwnerRename the team — the new name shows everywhere (invites, activity log). Owner only; audited.
GET /api/team/:groupId/members
?page= &per_page= &sort=name|role|courses|progress &dir= &roles= &q=
Viewer+The roster: members with per-course content-progress %, plus pending invites. roles= is a comma-separated filter over owner,manager,viewer,member (e.g. roles=member for learners, roles=owner,manager,viewer for leadership); q= searches name/email (3-char min).
GET /api/team/:groupId/members/:userIdViewer+One member's detail: profile, role, every team enrollment with progress/status, and (Manager+) the active certificate's verification code.
POST /api/team/:groupId/assign
{course_slug, emails?[], member_ids?[], self?, csv?}
Manager+Assign a course to up to 40 recipients: existing members are enrolled, new emails get seat-reserving invites. Idempotent; refuses over-allocation per recipient. Returns {assigned[], invited[], skipped[{email, reason}]}.
POST /api/team/:groupId/assign/preview
same body as assign
Manager+Dry run of assign — classifies every recipient (enroll / invite / skip + reason, with seat exhaustion simulated in order) and writes nothing.
POST /api/team/:groupId/members/invite
{email, role}
Manager+Role-only invite (Viewer/Manager/Owner) — joins the leadership on next login, no seat used. Only an Owner may grant owner.
POST /api/team/:groupId/members/:userId/role
{role}
Manager+Promote/demote. Managers can't touch Owners or other Managers; a team always keeps ≥ 1 Owner (409 otherwise).
DELETE /api/team/:groupId/members/:userIdManager+Remove a member: withdraws their unfinished team enrollments (frees seats), keeps earned certificates. Re-adding later restores progress.
DELETE /api/team/:groupId/members/:userId/courses/:courseSlugManager+Unenroll one course (frees its seat), keeping the membership and other courses. Completed courses are spent → 404.
DELETE /api/team/:groupId/invites/:inviteIdManager+Revoke a pending invite; its reserved seat returns to the pool.
POST /api/team/:groupId/invites/:inviteId/resendManager+Re-arm a pending or lapsed invite for a fresh 30 days and re-email the link. Reviving a lapsed invite re-reserves a seat (409 if none left).
GET /api/team/:groupId/audit
?limit= &before=
Manager+The activity log, newest first with keyset pagination ({entries[], next_before}).
GET /api/team/:groupId/exportViewer+Compliance CSV: one row per teacher per course — status, the enrolled-for state and what its certificate is worth (hours, plus PDPs where that state awards them; completed only), certificate code + issue/expiry.

Data export

Export the underlying learning-event trail (every meaningful action — enroll, complete, quiz pass/fail, certificate issue — as an append-only, xAPI-shaped record) joined with its enrollment, course, and certificate context. Add ?format=csv (default, spreadsheet-friendly) or ?format=xapi (an {statements:[…]} array for an LRS). A very large export is capped and flagged with an X-Export-Truncated response header.

EndpointWhoWhat it does
GET /api/me/export
?format=csv|xapi
any learnerYour own complete learning history — the data-portability / GDPR export. Only ever your events; never anyone else's.

Certificate verification

Every certificate carries a verification code (it's in the compliance CSV and on the certificate itself). Anyone — an HR office, a license reviewer — can confirm a certificate at https://learn.couchcertified.com/verify/<code> with no account.

Example: pull your roster

# 1. find your team id
curl -s https://learn.couchcertified.com/api/team/mine \
  -H "Authorization: Bearer $TOKEN"
# → {"teams":[{"group_id":12,"uuid":"…","name":"Demo District","role":"owner"}]}

# 2. roster, sorted by progress
curl -s "https://learn.couchcertified.com/api/team/12/members?sort=progress&dir=desc" \
  -H "Authorization: Bearer $TOKEN"

# 3. compliance CSV
curl -s https://learn.couchcertified.com/api/team/12/export \
  -H "Authorization: Bearer $TOKEN" -o team-progress.csv