Install our app for a better experience!

Target Score Plans through the API

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" or 1/0, as elsewhere in the API.
  • An empty value ("" or null) 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 a timezone.
  • target_score, exam_date and safe_exam_browser work on any course. test_plan and 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 400 and 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: …