Errors
The error envelope, every error code, and how to react to each.
Every error — from any endpoint — returns the same envelope, so one handler covers the whole API.
The Envelope
{
"errors": [
{
"status": "422",
"code": "validation_error",
"title": "Unprocessable Entity",
"detail": "Unknown filter 'shcool_id'"
}
]
}status
The HTTP status, as a string.
code
Branch on this. A stable machine-readable identifier.
title
The standard HTTP status name.
detail
Human-readable specifics. Helpful in logs; do not parse.
errors is always an array, though today it always holds exactly one entry. Treat it as a list so your parser survives multi-error responses later.
Branch on code, never on detail. Codes are part of the contract; detail strings are prose and may be reworded at any time.
Status Codes
200
Success.
401
Not authenticated — credentials or token problem.
403
Authenticated, but not allowed.
404
No such record in your district.
422
The request was understood but a parameter is invalid.
429
Rate limit exceeded.
5xx
Server-side problem. Retry with backoff.
Error Codes
401 — Authentication
invalid_token
Missing, malformed, expired or revoked token; or bad credentials at the token endpoint.
Fetch a new token and retry once. If it fails again, the credential is bad — stop.
expired_client
The credential has passed its expiry date.
Not recoverable in code. Request a new credential.
revoked_credential
The credential was revoked.
Not recoverable. Contact Support.
unknown_district
The credential's district can no longer be resolved.
Not recoverable. Contact Support.
not_authorized
Authorisation failed for this request.
Check the token is being sent correctly.
403 — Forbidden
feature_disabled
The API is not enabled for this district.
Contact Support to enable it. Retrying will not help.
insufficient_scope
The token lacks the scope this endpoint requires.
Check the scope table. On nested routes you need the child's scope.
forbidden
Access denied to this resource.
Do not retry.
404 — Not Found
not_found
No such record in your district — it may not exist, or it may belong to someone else. The API does not distinguish the two.
An unknown report name also returns not_found.
422 — Invalid Parameters
All parameter problems share the code validation_error; detail says which.
detail
Cause
Unknown filter 'x'
Not an allowed filter for this endpoint.
Unknown sort 'x'
Not a sortable column for this endpoint.
filter must be an object, e.g. filter[key]=value
Sent ?filter=x instead of ?filter[key]=x.
sort must be a comma-separated string, e.g. sort=name,-created_at
Sent ?sort[]=x instead of ?sort=x.
page[number] must be an integer / must be >= 1
Non-numeric or zero/negative page.
page[size] must be an integer / must be between 1 and 100
Non-numeric or out-of-range page size.
filter[updated_since] must be a valid ISO 8601 datetime
Malformed timestamp.
Missing required filter(s): filter[x]
A report's required filter was omitted.
unsupported grant_type
grant_type was not client_credentials.
429 — Rate Limited
Slow down and honour Retry-After. See Rate Limits & Policies.
Handling Errors
A robust client distinguishes three classes:
Retry immediately, once. 401 invalid_token — your token probably just expired. Refresh and replay. If the replay also fails, stop; something is wrong with the credential, and hammering it wastes your token budget.
Retry later, with backoff. 429 and 5xx. Wait for Retry-After (or exponential backoff for 5xx), then retry. Cap your attempts.
Do not retry. 403, 404 and 422 are deterministic — the same request will always fail the same way. Log it and fix the request or the credential.
Last updated
Was this helpful?