Target Score Plans through the API
For institutes that use the Target Score Tracker. The same call that onboards a student can also:
- set their target score and exam date,
- start, change or turn off their test plan (IELTS Academic, IELTS General Training and SAT), and
- require Safe Exam Browser for every test they are assigned.
It is one optional field, target_score_plan, on POST /provision/student/. A call that doesn't send it
works exactly as before, so nothing changes for students who are not on a plan.
Everything here is the same as doing it by hand in Student Management → Manage → Target score plan. The plan is the one a teacher would create, with the same tests, emails and alerts.
Start a plan while onboarding
curl -s -X POST https://<host>/api/v1/provision/student/ \
-H "X-API-Key: <key>" -H "Content-Type: application/json" \
-d '{
"email": "sara@example.com",
"first_name": "Sara",
"courses": ["sat"],
"tenure_months": 6,
"external_ref": "wc_order_2101",
"target_score_plan": {
"target_score": 1350,
"exam_date": "2026-12-10",
"test_plan": true,
"timezone": "Asia/Dubai",
"pace": "standard",
"safe_exam_browser": true
}
}'
The response is the usual onboarding response with one more block (shortened):
{
"status": "success",
"created_user": true,
"enrollments": [ { "course_type": "sat", "course_id": 1753, "enrollment_id": 2262,
"action": "created", "end_date": "2027-04-11" } ],
"target_score_plan": {
"course_type": "sat",
"course_id": 1753,
"enrollment_id": 2262,
"target_score": 1350,
"exam_date": "2026-12-10",
"safe_exam_browser": true,
"test_plan_available": true,
"test_plan": {
"status": "active",
"exam": "sat",
"pace": "standard",
"custom_interval_days": null,
"current_pace": "standard",
"timezone": "Asia/Dubai",
"section_minimums": {},
"practice_drills": true,
"staff_recipients": ["owner@acme-prep.com"],
"extra_recipients": [],
"at_risk": false,
"started_at": "2026-10-11T17:46:15+00:00",
"next_test": { "kind": "baseline", "section": null, "opens_on": "2026-10-11",
"due_on": "2026-10-17", "state": "waiting" }
},
"action": "created",
"changes": ["safe_exam_browser", "test_plan"]
}
}
What happened: Sara's account and SAT course were created as usual. Safe Exam Browser was switched on
for her, then her test plan started. Her first test (a full baseline mock, on a paper she has never seen)
is assigned now, opens today and is due in a week. She gets the plan's own notice and a "your test is open"
email, separate from the welcome email, and her result is emailed to the staff in staff_recipients.
The fields
Everything inside target_score_plan is optional. Send only what you want to set.
| Field | Type | What it does |
|---|---|---|
course |
string or number | Which course of this call the settings are for: a course type ("sat") or a course id. Needed only when the call names more than one course. |
target_score |
number | The student's target. IELTS: 0–9 in steps of 0.5. SAT: 400–1600 in steps of 10. Other courses: the course type's range, shown as target_score_min and target_score_max in GET /courses/ (for example PTE Academic 10–90, TOEFL 0–120). |
exam_date |
YYYY-MM-DD |
The date of the real exam. Plan tests stop 3 days before it. |
test_plan |
boolean | true starts the test plan (or keeps it running). false turns it off. Leave it out to leave the plan as it is. |
timezone |
string | Needed to start a plan. A zone name such as Asia/Dubai or Europe/London. Tests open at 00:00 and are due at 23:59 in this zone, and emails show it. |
pace |
string | auto (default: the plan adapts to the student's results), intensive (every 4 days), standard (every 7), light (every 10), or custom. |
custom_interval_days |
whole number | With pace: "custom": the days between tests, 1–14. |
section_minimums |
object | Lowest acceptable score per section, e.g. {"writing": 6.5}. IELTS sections: listening, reading, writing, speaking. SAT: reading_writing, math (200–800 in steps of 10). Replaces the minimums already set; {} removes them. |
practice_drills |
boolean | Short practice drills on the weakest skills after each counted test. On unless you send false. |
staff_recipients |
list of emails | Your admins or examiners who get each result and the alerts. Replaces the list. Left out when starting a plan, it is your organization's owner if they are one of your admins or examiners, otherwise all your admins. |
extra_recipients |
list of emails | Up to 5 other addresses, such as a parent or an agent. They get the same results and alerts as a summary, without a link to the result (they cannot sign in). Replaces the list. |
safe_exam_browser |
boolean | true: every test this student is assigned opens only in Safe Exam Browser, plan tests included. false turns it off. Tests already started are never changed. |
- Lists may also be sent as text:
"a@x.com, b@x.com". - Booleans may be
true/false,"yes"/"no"or1/0, as elsewhere in the API. - An empty value (
""ornull) means "not sent" and changes nothing. A no-code tool that sends empty fields can never wipe a target by accident. To clear a target, use Student Management. - Starting a plan needs a
target_score(unless the student already has one) and atimezone. target_score,exam_dateandsafe_exam_browserwork on any course.test_planand the plan settings work on IELTS Academic, IELTS General Training and SAT courses.
Change a plan later
Send the call again for the same student and course, with only what changes. Everything you leave out stays as it is. For example, when the exam moves:
{ "email": "sara@example.com", "first_name": "Sara", "courses": ["sat"],
"target_score_plan": { "exam_date": "2026-12-25" } }
{ "status": "success",
"target_score_plan": { "exam_date": "2026-12-25", "action": "updated", "changes": ["exam_date"],
"test_plan": { "status": "active", "pace": "standard", "timezone": "Asia/Dubai" } } }
A test that is waiting and not yet open is replaced by one dated for the new settings. A test already open keeps its opening day, and its due date only moves earlier if the new exam date needs it. A test the student has started is never touched.
Safe to repeat. The same call again changes nothing: "action": "unchanged", "changes": [], and
status is already_provisioned when nothing else changed either. A retried webhook never adds a second
plan, a second test or a second email.
Turn a plan off
{ "email": "sara@example.com", "first_name": "Sara", "courses": ["sat"],
"target_score_plan": { "test_plan": false } }
{ "status": "success",
"target_score_plan": { "target_score": 1350, "exam_date": "2026-12-25", "test_plan": null,
"action": "closed", "changes": ["test_plan"] } }
The waiting test is withdrawn. A test the student has already started can still be finished. All results and history are kept, and the target stays on the course.
A target without a plan
On any course, for example PTE Academic, send just the target. This is what Student Management → Target sets. Omar is onboarded with a target of 79:
{ "email": "omar@example.com", "first_name": "Omar", "courses": ["pte_academic"],
"target_score_plan": { "target_score": 79, "exam_date": "2026-12-01" } }
{ "status": "success", "created_user": true,
"target_score_plan": { "course_type": "pte_academic", "target_score": 79, "exam_date": "2026-12-01",
"safe_exam_browser": false, "test_plan_available": false, "test_plan": null,
"action": "updated", "changes": ["target_score", "exam_date"] } }
While a test plan is on, the target lives on the plan, so the same fields change the plan's target.
Safe Exam Browser for one student
Safe Exam Browser works on any course, with or without a plan. Switching it on for Omar:
{ "email": "omar@example.com", "first_name": "Omar", "courses": ["pte_academic"],
"target_score_plan": { "safe_exam_browser": true } }
{ "status": "success",
"target_score_plan": { "course_type": "pte_academic", "target_score": 79, "safe_exam_browser": true,
"action": "updated", "changes": ["safe_exam_browser"] } }
Omar's tests that have not started switch over, and every test assigned to him from now on needs
it. Their assignment emails tell them so and link the device check. Safe Exam Browser must be switched on
for your site: GET /organization/ shows "safe_exam_browser_available".
Which course it applies to
The settings go on a course named in the same call, the one the call granted or kept. For a student
who already has the course, just name it again: "courses": ["sat"] leaves running access unchanged.
When the call names one course, that's the one. When it names several, add course. Lina is onboarded
for SAT and IELTS, with her plan on SAT:
{ "email": "lina@example.com", "first_name": "Lina", "courses": ["sat", "ielts"],
"target_score_plan": { "course": "sat", "target_score": 1350, "test_plan": true, "timezone": "Asia/Dubai" } }
{ "status": "success", "created_user": true,
"enrollments": [ { "course_type": "sat", "action": "created" }, { "course_type": "ielts", "action": "created" } ],
"target_score_plan": { "course_type": "sat", "target_score": 1350, "exam_date": null,
"action": "created", "changes": ["test_plan"],
"test_plan": { "status": "active", "pace": "auto", "next_test": { "kind": "baseline" } } } }
With no exam_date, the plan runs until the course's access ends.
A student can have one plan per exam. For plans on two exams, send one call per course.
Reading it back
GET /students/?email=: every enrolment now carries a target_score_plan block in the same shape as
above (without action and changes). test_plan is null when no plan is running. Sara's SAT course,
after her plan was turned off:
{ "course_type": "sat", "course_id": 1753, "enrollment_id": 2262, "status": "active",
"target_score_plan": { "target_score": 1350, "exam_date": "2026-12-25", "safe_exam_browser": true,
"test_plan_available": true, "test_plan": null } }
GET /organization/: which course types take a test plan, and whether Safe Exam Browser is available:
{ "target_score_plans": { "test_plan_course_types": ["ielts", "ielts_general", "sat"],
"safe_exam_browser_available": true } }
GET /courses/: each entry in course_types[] has test_plan_available, and the target range for that
course type as target_score_min and target_score_max:
{ "name": "pte_academic", "display_name": "PTE Academic", "test_plan_available": false,
"target_score_min": 10.0, "target_score_max": 90.0 }
target_score_plan in the response
| Field | Meaning |
|---|---|
course_type, course_id, enrollment_id |
The course the settings are on. |
target_score, exam_date |
The target and exam date now in force: the plan's while a plan is on, otherwise the course's. |
safe_exam_browser |
Whether this student's tests need Safe Exam Browser. |
test_plan_available |
true for IELTS Academic, IELTS General Training and SAT courses. |
test_plan |
null when no plan is running. Otherwise its settings, status (active or paused), current_pace (the pace the plan is using now), at_risk and next_test (kind: baseline, mock, skills_check or challenge; state: waiting, started or unfilled when no unseen paper is left). |
action |
created (plan started), updated, closed (plan turned off), unchanged, or skipped (the course is suspended). |
changes |
What this call changed, e.g. ["exam_date"]. |
Rules worth knowing
- All or nothing. The call is checked before anything is saved. A rule that depends on the student's
own plan, such as an exam date too close or a second plan for the same exam, is checked in the same step
as the onboarding. A refusal returns
400and saves nothing, not even a new account. - Exam dates. A plan needs at least 7 days before its last test day (3 days before the exam, or the day before the course's access ends, whichever is earlier). Sooner than that, assign a mock directly.
- Suspended course. Nothing is applied:
"action": "skipped"and a warning, like the rest of the call. - Pause, skip, open a test today, record the official result: do these on the student's plan page in the platform.
- The plan's changes appear in the plan's history as made by
API key '<your key name>'. - A read-only key can't change any of this (
403).
Errors
All are 400 with { "status": "error", "error": "<reason>" }, and nothing is saved.
| When | Example error |
|---|---|
| Score off the exam's scale | 'target_score_plan.target_score': an SAT target is 400–1600 in steps of 10. |
| A test plan on another course type | Test plans cover IELTS Academic, IELTS General Training and SAT, not PTE Academic. … |
| Starting a plan without a time zone | 'target_score_plan.timezone' is needed to start a test plan, for example "Asia/Dubai". … |
| Plan settings with no plan running | This student has no test plan for this course, so pace cannot be set. Send 'test_plan': true to start one. |
| Exam date too close | 'target_score_plan': Too close for a plan — assign a mock directly. |
| A second plan for the same exam | 'target_score_plan': This student already has an active plan for this exam on another enrollment. |
Several courses, no course |
This call names more than one course, so say which one 'target_score_plan' is for … |
| A recipient who is not staff | 'target_score_plan.staff_recipients': x@y.com is not an active admin or examiner of your organization. … |
| Safe Exam Browser off for the site | 'target_score_plan.safe_exam_browser': Safe Exam Browser is switched off for this site … |
| A misspelt field | Unknown field in 'target_score_plan': target_scor. Valid fields: … |
