ABBLEARN API reference
Version 1.0.0 · OpenAPI 3.1.0
Getting started
Read and manage a portal's people, courses, enrolments, results and certificates. Every request is made with a portal API key, created by an admin under Administration → API keys.
Send the key as a bearer token. The key decides which portal you are working with.
curl https://abblearn.com/api/v1/enrolments?status=active \ -H "Authorization: Bearer abl_…" \ -H "Accept: application/json"
Conventions
Each endpoint needs one permission on the key, shown next to it below. Missing it returns 403.
Lists return up to per_page items (default 25, at most 100). Pass meta.next_cursor as ?cursor= until it is null.
120 requests a minute per key. Over that you get 429 with a Retry-After header.
Send an Idempotency-Key header with POST and PATCH. Repeating the request within 24 hours returns the first response instead of acting twice.
Errors are JSON with a message. Validation errors (422) also list errors per field; reaching a plan limit is reported under errors.plan.
Times are ISO 8601 in UTC. Ids are integers, except the portal id (a UUID).
Portal
The portal the key belongs to.
/portal
The portal: details, plan limits and current usage
Returns Portal · responses: 200, 401, 403, 422, 429
/billing
Billing state, plan, trial and grace period
Returns Billing · responses: 200, 401, 403, 422, 429
People
Members of the portal.
/users
People in the portal
| Parameter | In | Description |
|---|---|---|
role |
query | owner, admin, manager, instructor or learner |
status |
query | active, invited or suspended |
email |
query | Exact email address (case-insensitive) |
per_page |
query | Results per page (1–100, default 25). |
cursor |
query | The `next_cursor` from the previous page. |
Returns Member[] · responses: 200, 401, 403, 422, 429
/users
Invite someone; they join when they accept the emailed invitation
| Parameter | In | Description |
|---|---|---|
Idempotency-Key |
header | Any unique string. Repeating the request with the same key within 24 hours returns the first response (with `Idempotent-Replayed: true`) instead of acting again. |
JSON body
-
emailstring, required -
rolestring, required · admin, manager, instructor, learner -
namestring · Pre-fills the acceptance form.
Returns Invitation · responses: 202, 401, 403, 409, 422, 429
/users/{user}
One person
| Parameter | In | Description |
|---|---|---|
user |
path | The person's id |
Returns Member · responses: 200, 401, 403, 404, 422, 429
Courses
Courses, sections and lessons.
/courses
Courses in the portal
| Parameter | In | Description |
|---|---|---|
status |
query | draft, in_review, published or archived |
per_page |
query | Results per page (1–100, default 25). |
cursor |
query | The `next_cursor` from the previous page. |
Returns Course[] · responses: 200, 401, 403, 422, 429
/courses/{course}
One course with its sections and lessons
| Parameter | In | Description |
|---|---|---|
course |
path | The course id |
Returns Course · responses: 200, 401, 403, 404, 422, 429
Enrolments
Who is studying what, and how far they are.
/enrolments
Enrolments
| Parameter | In | Description |
|---|---|---|
user_id |
query | Only this person |
course_id |
query | Only this course |
status |
query | active, completed, expired, suspended or withdrawn |
updated_since |
query | Only enrolments changed since this time (ISO 8601) |
per_page |
query | Results per page (1–100, default 25). |
cursor |
query | The `next_cursor` from the previous page. |
Returns Enrolment[] · responses: 200, 401, 403, 422, 429
/enrolments
Enrol an active member on a published course
| Parameter | In | Description |
|---|---|---|
Idempotency-Key |
header | Any unique string. Repeating the request with the same key within 24 hours returns the first response (with `Idempotent-Replayed: true`) instead of acting again. |
JSON body
Identify the learner by `user_id` or `email`.
-
course_idinteger, required -
user_idinteger -
emailstring -
due_atstring · Optional deadline, in the future.
Returns Enrolment · responses: 201, 200, 401, 403, 409, 422, 429
/enrolments/{enrolment}
One enrolment with progress through each lesson
| Parameter | In | Description |
|---|---|---|
enrolment |
path | The enrolment id |
Returns Enrolment · responses: 200, 401, 403, 404, 422, 429
/enrolments/{enrolment}
Withdraw a learner (the only change allowed)
| Parameter | In | Description |
|---|---|---|
enrolment |
path | The enrolment id |
Idempotency-Key |
header | Any unique string. Repeating the request with the same key within 24 hours returns the first response (with `Idempotent-Replayed: true`) instead of acting again. |
JSON body
-
statusstring, required · withdrawn
Returns Enrolment · responses: 200, 401, 403, 404, 409, 422, 429
Results
Graded assessment attempts.
/assessment-results
Graded assessment attempts (answers are not included)
| Parameter | In | Description |
|---|---|---|
user_id |
query | Only this person |
course_id |
query | Only this course |
passed |
query | true or false |
per_page |
query | Results per page (1–100, default 25). |
cursor |
query | The `next_cursor` from the previous page. |
Returns AssessmentResult[] · responses: 200, 401, 403, 422, 429
Certificates
Certificates issued to learners.
/certificates
Certificates issued in the portal
| Parameter | In | Description |
|---|---|---|
user_id |
query | Only this person |
course_id |
query | Only this course |
status |
query | issued, expired, revoked or superseded |
per_page |
query | Results per page (1–100, default 25). |
cursor |
query | The `next_cursor` from the previous page. |
Returns Certificate[] · responses: 200, 401, 403, 422, 429
/certificates/{certificate}
One certificate
| Parameter | In | Description |
|---|---|---|
certificate |
path | The certificate id |
Returns Certificate · responses: 200, 401, 403, 404, 422, 429
Objects
idstringnamestringurlstringstatusstringlocalestringtimezonestringcreated_atstring | nullplanobject | nullusageobject
statestringplanstring | nulltrial_ends_atstring | nullgrace_period_ends_atstring | nullsubscriptionobject | null
idintegernamestringemailstringrolestringstatusstringjob_titlestring | nulldepartmentstring | nullexternal_idstring | nulljoined_atstring | nulllast_visit_atstring | null
emailstringrolestringinvitation_expires_atstring | null
idintegertitlestringsummarystring | nullstatusstringvisibilitystringcategorystring | nullestimated_duration_minutesinteger | nullawards_certificatebooleanlessons_countintegerpublished_atstring | nullupdated_atstring | nullsectionsarray · Only on GET /courses/{course}.
idintegeruserobjectcourseobjectstatusstringsourcestringprogress_percentintegeris_overduebooleanenrolled_atstring | nullstarted_atstring | nulldue_atstring | nullcompleted_atstring | nullexpires_atstring | nulllast_visit_atstring | nullupdated_atstring | nulllessonsarray · Only on GET /enrolments/{enrolment}.
idintegeruserobjectassessmentobjectattemptintegerstatusstringscore_percentnumber | nullpassedbooleanstarted_atstring | nullsubmitted_atstring | nullgraded_atstring | null
idintegerverification_codestringverification_urlstringuser_idintegerrecipient_namestringcourseobjectstatusstringscore_percentnumber | nullissued_atstring | nullexpires_atstring | nullrevoked_atstring | null
Webhooks
Add endpoints under Administration → Webhooks. ABBLEARN sends a JSON POST for each event you choose, retries failures for about two days, and switches an endpoint off after repeated failures.
| Event | Sent when |
|---|---|
enrolment.created | A learner is enrolled on a course |
enrolment.completed | A learner completes a course |
enrolment.withdrawn | A learner is withdrawn from a course |
certificate.issued | A certificate is issued |
certificate.revoked | A certificate is revoked |
billing.status_changed | The subscription status changes |
billing.payment_failed | A subscription payment fails |
ping | Test event |
Example request
POST /your/endpoint
Content-Type: application/json
ABBLEARN-Event-Id: 0b8e5f0e-4c1a-4a53-9a55-0d2f1c8b9e41
ABBLEARN-Event-Type: enrolment.completed
ABBLEARN-Timestamp: 1767225600
ABBLEARN-Signature: t=1767225600,v1=5f2b…
{"id":"0b8e5f0e-4c1a-4a53-9a55-0d2f1c8b9e41","type":"enrolment.completed","created_at":"2026-01-01T00:00:00+00:00","data":{"enrolment":{"id":42,"status":"completed", …}}}
Checking the signature
Compute an HMAC-SHA256 of "{timestamp}.{raw body}" with your endpoint secret, compare it with v1 in constant time, and reject timestamps more than five minutes old. Ignore events whose id you have already handled.
// PHP
[$t, $v1] = sscanf($_SERVER['HTTP_ABBLEARN_SIGNATURE'], 't=%d,v1=%s');
$body = file_get_contents('php://input');
$valid = abs(time() - $t) <= 300
&& hash_equals(hash_hmac('sha256', $t.'.'.$body, $secret), $v1);
// Node.js
const [, t, v1] = req.get('ABBLEARN-Signature').match(/^t=(\d+),v1=([a-f0-9]{64})$/);
const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
const valid = Math.abs(Date.now() / 1000 - Number(t)) <= 300
&& crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));