Access Duration, Renewals & Expiry
Every course you grant through the API has an access window. This page explains how that window is set, what the student experiences before and after it ends, and how to renew or extend it. All dates below come from real API responses.
How the window is set
| Date in the response | How it is worked out |
|---|---|
start_date |
The day of the call. Access starts immediately. |
end_date |
start_date plus the number of months, in calendar months. 17 Sep 2026 + 6 months = 17 Mar 2027. |
grace_period_end |
end_date plus your organization's grace days. This is the last day the student can still practise. |
How many months? The first of these that applies:
tenure_monthsin the call (1–120).- For a course chosen with
course_ids: that course's own default duration (courses[].default_access_months). - Your organization's default (
provisioning_defaults.default_access_monthsfromGET /organization/).
Grace days come from your organization's settings (provisioning_defaults.grace_period_days). With a
grace of 0, grace_period_end equals end_date.
The timeline, with real dates
Jane was onboarded on 17 Sep 2026 with "tenure_months": 6, in an organization with 30 grace days:
17 Sep 2026 17 Mar 2027 16 Apr 2027
start_date ───────────▶ end_date ──── grace period ──▶ grace_period_end ──▶ access removed
full access full access continues last day from the next day
| When | What the student experiences | status |
has_access |
|---|---|---|---|
Up to end_date |
Full access to the course. | active |
true |
After end_date, up to grace_period_end |
Still full access. | grace_period |
true |
After grace_period_end |
Can still sign in and see all past results. Cannot start a new test. | expired |
false |
- A test already in progress when access ends can always be finished, and the result is delivered.
- Emails to students only ever mention
end_date, never the grace period. - If your organization has access reminder emails switched on, the student is reminded before access ends.
- The platform checks dates daily, so
statuschanges shortly after a date passes. The dates andhas_accessin the API always reflect the dates, whatever the storedstatussays at that moment.
Renewing after access ended
Send the same call again, with the new duration. If the course's end_date has passed (including during
the grace period, or after access was removed), the same enrolment gets a fresh window from today. No
duplicate is created, and the student keeps all their past results.
{ "email": "ali@example.com", "first_name": "Ali", "course_ids": [371],
"tenure_months": 6, "external_ref": "wc_order_1600" }
Ali's access had ended 45 days earlier. Shortened response:
{
"status": "success",
"enrollments": [
{ "course_id": 371, "course_name": "IELTS Weekend Batch", "action": "reactivated",
"status": "active", "start_date": "2026-09-17", "end_date": "2027-03-17",
"grace_period_end": "2027-04-16", "has_access": true, "starter_tests_assigned": 1 }
],
"email": { "status": "sent", "type": "welcome", "detail": "" }
}
Extending while access is still running
A plain repeat call does not add time to a course that is still running. That is what makes retries
safe. To add time, say so explicitly with extend_access and give the purchase a reference:
{ "email": "jane@example.com", "first_name": "Jane", "courses": ["ielts"],
"tenure_months": 3, "extend_access": true, "external_ref": "wc_order_1500" }
Jane's IELTS access was due to end on 17 Mar 2027. Shortened response:
{
"status": "success",
"enrollments": [
{ "course_type": "ielts", "action": "extended", "status": "active",
"start_date": "2026-09-17", "end_date": "2027-06-17", "grace_period_end": "2027-07-17" }
]
}
The three months were added to the current end date, not to today. Sending the same call again (for example a retried webhook) changes nothing:
{ "status": "already_provisioned",
"enrollments": [ { "action": "unchanged", "end_date": "2027-06-17",
"note": "already_extended_for_external_ref" } ] }
Why external_ref is required: each reference extends a given course at most once. Use your
order or payment id, so a genuine second purchase (new id) extends again and a retry (same id) does not.
What each action means
action |
What the call did to this course |
|---|---|
created |
New enrolment with a fresh window. |
reactivated |
The window had ended; the same enrolment now has a fresh window from today. |
extended |
Time was added to a window that was still running (extend_access). |
unchanged |
The window is still running and nothing was requested, so nothing changed. |
skipped |
Left alone on purpose. skipped_reason: "suspended" means an admin suspended this student. |
already_enrolled is true for unchanged and skipped, and false otherwise.
Statuses you may see
status |
Meaning |
|---|---|
active |
Inside the access window. |
grace_period |
After end_date, still has access until grace_period_end. |
pending |
Scheduled to start later (only set from inside the platform). |
expired |
Access removed after the grace period. Renew with another call. |
suspended |
An admin suspended the student. The API never lifts a suspension; do it in the platform. |
dropped / completed |
Ended from inside the platform. Renew with another call. |
