Troubleshooting & Security
Troubleshooting
| Symptom | Cause / fix |
|---|---|
401 Invalid or missing API key |
Key missing, mistyped, revoked, or in the wrong header. It must be X-API-Key. |
403 not enabled for language tests |
The organization doesn't have language tests enabled. A platform admin must enable it before provisioning. |
400 Unknown course type '…' |
Use an exact value from GET /courses/ (e.g. pte_academic, not pte). Only active course types are accepted. |
400 'email'/'first_name'/'courses' is required |
A required field is empty or not mapped from the trigger. |
| Student not in the course | Confirm the response enrollments[] lists the course; check Django admin → Integrations → Provisioning Logs for that email. |
| Student didn't get the email | Check welcome_email_sent in the response; you can always resend using set_password_url, or send your own email with send_welcome_email: false. |
| A returning student isn't getting access | If their enrollment is suspended, provisioning leaves it untouched (skipped_reason: suspended). Lift the suspension in the admin. |
| Duplicate calls | Harmless — the API is idempotent; a repeat returns already_provisioned. |
Provisioning logs
Every call made with an API key — success, failure, or an invalid/revoked-key 401 — is recorded
in Django admin → Integrations → Provisioning Logs (email, courses, result, source, IP, time).
(Anonymous probes with no key at all are not persisted.) This is the first place to look when an
integrator says "it didn't work."
Security
- Always call over HTTPS.
- Treat the API key like a password. Use a separate key per automation tool so you can revoke one without affecting the others.
- Rotate keys periodically: create a new key, switch your automation to it, then revoke the old one.
- The key scopes access to your organization only — it cannot read or write any other organization's data.
- Only a SHA-256 hash of each key is stored; the raw key is shown once at creation and never again.
- Secret-looking fields in a request payload (anything containing
password,secret,token,api_key,credential, …) are masked before the payload is written to the audit log.
