ABBLEARN

ABBLEARN API reference

Version 1.0.0 · OpenAPI 3.1.0

OpenAPI spec (JSON)

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

Permissions

Each endpoint needs one permission on the key, shown next to it below. Missing it returns 403.

Paging through results

Lists return up to per_page items (default 25, at most 100). Pass meta.next_cursor as ?cursor= until it is null.

Rate limit

120 requests a minute per key. Over that you get 429 with a Retry-After header.

Safe retries

Send an Idempotency-Key header with POST and PATCH. Repeating the request within 24 hours returns the first response instead of acting twice.

Errors

Errors are JSON with a message. Validation errors (422) also list errors per field; reaching a plan limit is reported under errors.plan.

Dates and ids

Times are ISO 8601 in UTC. Ids are integers, except the portal id (a UUID).

Portal

The portal the key belongs to.

GET
/portal
tenant:read

The portal: details, plan limits and current usage

Returns Portal · responses: 200, 401, 403, 422, 429

GET
/billing
billing:read

Billing state, plan, trial and grace period

Returns Billing · responses: 200, 401, 403, 422, 429

People

Members of the portal.

GET
/users
users:read

People in the portal

ParameterInDescription
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

POST
/users
users:write

Invite someone; they join when they accept the emailed invitation

ParameterInDescription
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

  • email string, required
  • role string, required · admin, manager, instructor, learner
  • name string · Pre-fills the acceptance form.

Returns Invitation · responses: 202, 401, 403, 409, 422, 429

GET
/users/{user}
users:read

One person

ParameterInDescription
user path The person's id

Returns Member · responses: 200, 401, 403, 404, 422, 429

Courses

Courses, sections and lessons.

GET
/courses
courses:read

Courses in the portal

ParameterInDescription
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

GET
/courses/{course}
courses:read

One course with its sections and lessons

ParameterInDescription
course path The course id

Returns Course · responses: 200, 401, 403, 404, 422, 429

Enrolments

Who is studying what, and how far they are.

GET
/enrolments
enrolments:read

Enrolments

ParameterInDescription
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

POST
/enrolments
enrolments:write

Enrol an active member on a published course

ParameterInDescription
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_id integer, required
  • user_id integer
  • email string
  • due_at string · Optional deadline, in the future.

Returns Enrolment · responses: 201, 200, 401, 403, 409, 422, 429

GET
/enrolments/{enrolment}
progress:read

One enrolment with progress through each lesson

ParameterInDescription
enrolment path The enrolment id

Returns Enrolment · responses: 200, 401, 403, 404, 422, 429

PATCH
/enrolments/{enrolment}
enrolments:write

Withdraw a learner (the only change allowed)

ParameterInDescription
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

  • status string, required · withdrawn

Returns Enrolment · responses: 200, 401, 403, 404, 409, 422, 429

Results

Graded assessment attempts.

GET
/assessment-results
results:read

Graded assessment attempts (answers are not included)

ParameterInDescription
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.

GET
/certificates
certificates:read

Certificates issued in the portal

ParameterInDescription
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

GET
/certificates/{certificate}
certificates:read

One certificate

ParameterInDescription
certificate path The certificate id

Returns Certificate · responses: 200, 401, 403, 404, 422, 429

Objects

Portal
  • id string
  • name string
  • url string
  • status string
  • locale string
  • timezone string
  • created_at string | null
  • plan object | null
  • usage object
Billing
  • state string
  • plan string | null
  • trial_ends_at string | null
  • grace_period_ends_at string | null
  • subscription object | null
Member
  • id integer
  • name string
  • email string
  • role string
  • status string
  • job_title string | null
  • department string | null
  • external_id string | null
  • joined_at string | null
  • last_visit_at string | null
Invitation
  • email string
  • role string
  • invitation_expires_at string | null
Course
  • id integer
  • title string
  • summary string | null
  • status string
  • visibility string
  • category string | null
  • estimated_duration_minutes integer | null
  • awards_certificate boolean
  • lessons_count integer
  • published_at string | null
  • updated_at string | null
  • sections array · Only on GET /courses/{course}.
Enrolment
  • id integer
  • user object
  • course object
  • status string
  • source string
  • progress_percent integer
  • is_overdue boolean
  • enrolled_at string | null
  • started_at string | null
  • due_at string | null
  • completed_at string | null
  • expires_at string | null
  • last_visit_at string | null
  • updated_at string | null
  • lessons array · Only on GET /enrolments/{enrolment}.
AssessmentResult
  • id integer
  • user object
  • assessment object
  • attempt integer
  • status string
  • score_percent number | null
  • passed boolean
  • started_at string | null
  • submitted_at string | null
  • graded_at string | null
Certificate
  • id integer
  • verification_code string
  • verification_url string
  • user_id integer
  • recipient_name string
  • course object
  • status string
  • score_percent number | null
  • issued_at string | null
  • expires_at string | null
  • revoked_at string | 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.

EventSent when
enrolment.createdA learner is enrolled on a course
enrolment.completedA learner completes a course
enrolment.withdrawnA learner is withdrawn from a course
certificate.issuedA certificate is issued
certificate.revokedA certificate is revoked
billing.status_changedThe subscription status changes
billing.payment_failedA subscription payment fails
pingTest 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));