API Reference
Base URL: https://<your-platform-host>/api/v1/
All requests authenticate with your per-organization API key, sent on every request:
X-API-Key: <your-raw-key>
(Authorization: Bearer <your-raw-key> is also accepted — the same org key.)
Endpoints
| Method & path | Purpose |
|---|---|
GET /ping/ |
Test your key — returns your organization name. |
GET /courses/ |
List the valid courses values (course-type names). |
POST /provision/student/ |
Create + enrol a student. |
GET /ping/
curl -s https://<host>/api/v1/ping/ -H "X-API-Key: <key>"
{ "status": "ok", "organization": "Acme Prep", "organization_slug": "acme-prep" }
GET /courses/
curl -s https://<host>/api/v1/courses/ -H "X-API-Key: <key>"
{ "status": "ok", "course_types": [ { "name": "ielts", "display_name": "IELTS" } ] }
POST /provision/student/
Request body (JSON):
| Field | Required | Type | Notes |
|---|---|---|---|
email |
✅ | string | The student's email — used as the unique identity. |
first_name |
✅ | string | |
last_name |
– | string | |
courses |
– | string or array | One or more course-type names (see table below). e.g. "ielts" or ["ielts","gmat"]. Omit it to add the user as a bare org member with no enrollment (e.g. someone who will only use custom tests). |
tenure_months |
– | integer | Access length, 1–120. Defaults to your org's configured default. |
external_ref |
– | string | Your reference (e.g. the WooCommerce order id). Stored for traceability. |
send_welcome_email |
– | boolean | Default true. Set false to email the student yourself. |
source |
– | string | Optional label ("pabbly", "zapier", …) recorded in the audit log. |
Example:
{
"email": "[email protected]",
"first_name": "Jane",
"last_name": "Doe",
"courses": ["ielts", "gmat"],
"tenure_months": 6,
"external_ref": "wc_order_1234"
}
Success response (201 for a new account, 200 if the student already existed):
{
"status": "success",
"user_id": 4821,
"created_user": true,
"enrollments": [
{ "course_type": "ielts", "course_id": 12, "enrollment_id": 5567,
"status": "active", "start_date": "2026-06-04", "end_date": "2026-12-04",
"already_enrolled": false }
],
"login_url": "https://<host>/login/",
"set_password_url": "https://<host>/reset/MzQ/abc.../",
"welcome_email_sent": true
}
statusisalready_provisionedif nothing new was created (a safe retry).set_password_urlis returned only for new accounts — you may email it yourself if you setsend_welcome_email: false.
Error responses:
| HTTP | When | Body |
|---|---|---|
400 |
Bad payload (missing email/first_name/courses, unknown course type, bad tenure_months, invalid JSON) |
{ "status": "error", "error": "<reason>" } |
401 |
Missing/invalid/revoked API key | { "status": "error", "error": "Invalid or missing API key…" } |
403 |
The organization is not enabled for language tests | { "status": "error", "error": "This organization is not enabled…" } |
500 |
Unexpected server error — safe to retry | { "status": "error", "error": "Internal error while provisioning. Safe to retry." } |
Idempotency & retries
The endpoint is idempotent, so it's safe for automation tools that retry on failure:
- Idempotency is keyed on the student's email. Calling again never creates a duplicate account;
the repeat returns the same result with
"already_enrolled": true. - A returning student whose access had lapsed (expired/dropped) is re-activated with fresh dates — not duplicated.
- A suspended enrollment is never silently re-activated — provisioning leaves it as-is and
returns it with
"skipped_reason": "suspended". Lift the suspension in the admin to re-grant access. - Concurrent retries for the same student are safely serialized — no duplicate memberships or enrolments.
external_refis stored for traceability/correlation; it is not the idempotency key.- A
500is the only response you should retry. Never retry a400— fix the payload first (Pabbly/Zapier/Make treat4xxas a permanent failure by default, which is correct here).
Course-type reference
Use these exact values in courses. (Call GET /courses/ for the live, org-specific list — only
active types are provisionable.)
courses value |
Test |
|---|---|
ielts |
IELTS (Academic) |
ielts_general |
IELTS General Training |
toefl |
TOEFL |
pte_academic |
PTE Academic |
pte_core |
PTE Core |
oet |
OET |
gre |
GRE |
gmat |
GMAT |
sat |
SAT |
duolingo |
Duolingo English Test |
celpip |
CELPIP |
adaptive_language |
Adaptive Language Proficiency |
custom |
Custom Language Test |
