Install our app for a better experience!

Troubleshooting & Security

Troubleshooting & Security

Troubleshooting

Symptom Cause and fix
401 Invalid or missing API key The key is missing, mistyped or revoked, or sent in the wrong header. Send it as X-API-Key.
403 not enabled for language tests Language tests aren't enabled for your organization. Ask your platform administrator.
400 Unknown course type '…' Use a name from GET /courses/ (e.g. pte_academic, not pte). The error lists the valid values.
400 Course … was not found in your organization The course_ids value isn't one of your courses. Copy the id from GET /courses/.
400 Course … is not open for enrolment The course is draft, full, completed or past its end date, so it can't take a new or renewed student. Open it in the platform or pick another.
400 'tenure_months' must be a whole number… Send whole months from 1 to 120, e.g. 6.
400 'extend_access' needs an 'external_ref' Add your order id as external_ref.
400 A valid 'email' is required The email field is empty or not mapped from your trigger.
400 Username '…' is already taken Usernames are unique across the platform. Send a different one, or leave username out and one is made from the email address.
400 That 'password' can't be used It failed the platform's password rules: at least 8 characters, not all digits, not a common password, not too close to the email or name.
400 'password_mode' is 'set', so send the 'password' Either include the password, or use password_mode of generate.
A student's password wasn't changed Check warnings[]. An account that has been signed in to, or that came from outside this API, keeps its own username and password. An account you created here that nobody has used yet does accept them.
All three password_mode options behave the same You are probably re-testing with an email that already has a used account, so the mode is ignored every time. Test each mode with a fresh email address.
Student says they have no course Call GET /students/?email=…. Check has_access and the dates.
Student didn't get the email Look at email.status in the response. failed: call again (it retries) or send resend_welcome_email: true. suppressed: the platform won't mail that address (for example a test domain), so use a real one. sent: ask them to check spam.
"The set‑password link doesn't work" Links last 3 days and stop working once used. Send resend_welcome_email: true, or the student uses Forgot password with the same email.
A renewal didn't add time The course was still running. Send "extend_access": true with a new external_ref.
An extension didn't add time That external_ref already extended this course (note: already_extended_for_external_ref). A new purchase needs a new reference.
A student isn't getting access back Their enrolment is suspended (action: skipped). Lift the suspension in the platform.
Student can sign in but can't start a test Their access has ended (has_access: false). Renew with another call. Past results stay visible.
Duplicate calls Harmless: a repeat returns already_provisioned.

Audit log

Every call made with an API key is recorded on the server: success, failure, and wrong‑key attempts. Each entry has the email, courses, result, email outcome, source, IP address and time. Your platform administrator can look it up if a call "didn't work" but looked right to you.

Security

  • Always call over HTTPS.
  • Treat the key like a password. Use one key per tool, so revoking one doesn't break the others.
  • Rotate keys from time to time: create a new key, switch your automation to it, then revoke the old one.
  • A key only ever reaches your organization. The student lookup returns 404 for anyone who isn't your student, so a key can't be used to find out whether someone has an account elsewhere.
  • Only a SHA‑256 hash of each key is stored. The key itself is shown once, when you create it.
  • set_password_url is only returned for accounts your organization created through the API, and only until the student first signs in. For any other account the link goes to the student's own inbox only.
  • The API never changes the credentials of an account that is in use, so a key cannot be used to take over an account, and a repeated webhook cannot lock a student out. The one exception is an account your own key created that has never been signed in to, whose password you can already set through the set_password_url you were given.
  • A password you send is masked in the audit log and is not echoed back in the response. Only a password the platform generated is returned, so you can pass it to the student.
  • A mailed password is less safe than the set-password link, because it sits in the student's inbox. The link is the default for that reason; use the credentials modes when your own process needs them.
  • Fields in your request that look secret (names containing password, secret, token, api_key, credential, …) are masked before the request is written to the audit log.