Skip to content

External API

companyLEARN has a versioned REST API for integrations, for example to create courses from another system or to evaluate learning progress. It is intended for server-to-server calls and authenticates with API keys instead of a user sign-in. Calls from a browser page loaded from a different address are not supported.

The base URL is:

https://COMPANY.company-gpt.com/companygpt/learn/api/v1

Every API key belongs to the person who created it and acts on that person’s behalf:

  • Courses and videos that a key creates are attributed to that person.
  • The endpoints under /me return that person’s own enrollments and progress.
  • What a key may do follows from its scopes and that person’s permissions in companyLEARN, checked again on every request. If the person no longer has the Use courses permission, none of their keys work.

So grant only the scopes an integration actually needs, and revoke keys that are no longer needed.

People with the Manage courses permission create API keys in companyLEARN under API keys. Everyone sees only their own keys there.

  1. Click New key at the top right. The Create API key dialog opens.

  2. Under Name, enter a label you will recognize the integration by, for example course-seeder.

  3. Under Scopes, select at least one scope. courses:read is preselected.

  4. Under Expires, set an expiry date, tick Never expires, or leave the field empty. Without a date, a key expires after 90 days by default.

  5. Click New key.

  6. The Key created dialog shows the key. Copy it with Copy key and store it somewhere safe. Close the dialog with Done.

A key has the form learn_<prefix>_<secret>. The leading part learn_<prefix> is not secret; it appears in the list under Key and identifies the key. By default, each person can have up to 10 active keys.

The list under API keys shows Name, Key (the public part), Scopes, Status (Active, Revoked or Expired), Last used and Expires for each of your keys.

Revoke and the confirmation Revoke API key? disable a key. It stops working immediately; any integration using it receives 401 responses from then on. A revocation cannot be undone.

ScopeRequires Manage coursesAllows
courses:readnoRead courses, chapters and lessons, including unpublished ones, plus all endpoints under /me
courses:writeyesCreate, change, reorder and delete courses, chapters and lessons
quizzes:readyesRead quizzes including the answer key
quizzes:writeyesCreate, replace and delete quizzes
videos:readyesList and download videos
videos:writeyesUpload and delete videos
progress:readyesRead other people’s enrollments, progress and quiz attempts
enrollments:writeyesEnroll and unenroll other people
keys:readyesList your own API keys
keys:writeyesCreate and revoke keys – never with more scopes than the calling key

Keys created through the API can receive at most the scopes of the calling key. This lets you rotate keys automatically, but not create broader keys.

Send the key in one of these two headers:

Terminal window
curl -H "Authorization: Bearer learn_..." https://COMPANY.company-gpt.com/companygpt/learn/api/v1/courses
curl -H "X-API-Key: learn_..." https://COMPANY.company-gpt.com/companygpt/learn/api/v1/courses

GET /api/v1/me returns the key’s owner and the scopes the key actually has at that moment. GET /api/v1/openapi.json returns the full description of the API in OpenAPI 3.1 format, including the required scope for each endpoint. Both endpoints work with any valid key, regardless of its scopes.

The key in this example has the scopes courses:read and courses:write.

Terminal window
BASE=https://COMPANY.company-gpt.com/companygpt/learn/api/v1
KEY=learn_... # created under API keys
# 1. Check what the key may do
curl -s $BASE/me -H "Authorization: Bearer $KEY" | jq .apiKey.effectiveScopes
# 2. Create a course (starts as a draft)
CID=$(curl -s -X POST $BASE/courses -H "Authorization: Bearer $KEY" \
-H 'Content-Type: application/json' -d '{"title":"Onboarding"}' | jq -r .id)
# 3. Create a chapter
CHID=$(curl -s -X POST $BASE/courses/$CID/chapters -H "Authorization: Bearer $KEY" \
-H 'Content-Type: application/json' -d '{"title":"Week 1"}' | jq -r .id)
# 4. Create a text lesson
curl -s -X POST $BASE/chapters/$CHID/lessons -H "Authorization: Bearer $KEY" \
-H 'Content-Type: application/json' \
-d '{"title":"Welcome","type":"text","contentMarkdown":"# Welcome\n\nGood to have you here."}'
# 5. Publish
curl -s -X PATCH $BASE/courses/$CID -H "Authorization: Bearer $KEY" \
-H 'Content-Type: application/json' -d '{"isPublished":true}'

You create a quiz with the quizzes:write scope. PUT fully replaces an existing quiz and – as in the interface – resets all previous attempts:

Terminal window
curl -s -X PUT $BASE/chapters/$CHID/quiz -H "Authorization: Bearer $KEY" \
-H 'Content-Type: application/json' -d '{
"title": "Week 1 quiz",
"passThresholdPercent": 70,
"questions": [
{ "text": "Where do you find companyLEARN?",
"options": [
{ "text": "Under All apps", "isCorrect": true },
{ "text": "Only by email", "isCorrect": false }
] }
]
}'

All paths are relative to the base URL …/companygpt/learn/api/v1.

MethodPathScopeDescription
GET/meanyThe key’s owner and its effective scopes
GET/openapi.jsonanyDescription of the API (OpenAPI 3.1)
MethodPathScopeDescription
GET/coursescourses:readAll courses, including unpublished ones, with counters
POST/coursescourses:writeCreate a course
GET/courses/:idcourses:readCourse with its full outline
PATCH/courses/:idcourses:writeChange a course, e.g. isPublished or gatingEnabled (chapter gating)
DELETE/courses/:idcourses:writeDelete a course with all its content
GET/courses/:id/chapterscourses:readA course’s chapters
POST/courses/:id/chapterscourses:writeCreate a chapter
PUT/courses/:id/chapters/ordercourses:writeReorder chapters
PATCH/chapters/:idcourses:writeChange a chapter
DELETE/chapters/:idcourses:writeDelete a chapter
POST/chapters/:id/lessonscourses:writeCreate a lesson
PUT/chapters/:id/lessons/ordercourses:writeReorder lessons
GET/lessons/:idcourses:readGet a lesson
PATCH/lessons/:idcourses:writeChange a lesson
DELETE/lessons/:idcourses:writeDelete a lesson
MethodPathScopeDescription
GET/chapters/:id/quizquizzes:readQuiz including the answer key
PUT/chapters/:id/quizquizzes:writeCreate or replace a quiz
DELETE/chapters/:id/quizquizzes:writeDelete a quiz
MethodPathScopeDescription
GET/videosvideos:readList videos
POST/videosvideos:writeUpload a video (multipart field file)
GET/videos/:assetId/contentvideos:readDownload the video file (no partial download via Range)
DELETE/videos/:idvideos:writeDelete a video
MethodPathScopeDescription
GET/courses/:id/enrollmentsprogress:readEveryone enrolled in a course
POST/courses/:id/enrollmentsenrollments:writeEnroll a person, also in unpublished courses
DELETE/courses/:id/enrollments/:userIdenrollments:writeUnenroll a person; their progress is kept
GET/courses/:id/progressprogress:readProgress per person in a course
GET/quizzes/:id/attemptsprogress:readAll people’s attempts, filtered to one person with ?userId=
GET/learnersprogress:readAll known user IDs with activity counters
GET/learners/:userId/progressprogress:readOne person’s progress across all their courses

People are addressed by the user ID under which companyLEARN knows them; GET /learners lists all known IDs.

These endpoints correspond to the learner view and act for the key’s owner.

MethodPathScopeDescription
GET/me/coursescourses:readPublished courses
GET/me/courses/:idcourses:readA course’s outline with lock status
POST/me/courses/:id/enrollcourses:readEnroll
GET/me/lessons/:idcourses:readGet a lesson
POST/me/lessons/:id/completecourses:readMark a lesson as done
GET/me/progresscourses:readOwn progress
GET/me/quizzes/:idcourses:readQuiz without the answer key
POST/me/quizzes/:id/attemptscourses:readSubmit an attempt
GET/me/quizzes/:id/attemptscourses:readOwn previous attempts
MethodPathScopeDescription
GET/keyskeys:readList your own keys
POST/keyskeys:writeCreate a key; scopes at most those of the calling key
DELETE/keys/:idkeys:writeRevoke a key
StatusMeaning
401The key is missing, invalid, expired or revoked.
403 with INSUFFICIENT_SCOPEThe key lacks the required scope. The response names it in requiredScope and lists the effective scopes in grantedScopes.
403 with EXT_ACCESS_DENIEDThe key’s owner does not have the Use courses permission.
429Too many requests with this key. Each key has its own quota, by default 600 requests per minute; the response gives the length of the time window in seconds in retryAfter (60 by default).

Back to the overview.