Errors
Every non-2xx response carries the same envelope. Code against the code, which is stable; the message is for people and is translated.
The format
{
"error": {
"code": "not_found",
"message": "No encontrado",
"request_id": "01J9ZC…"
}
}Keep the request_id when something fails: it is how your request is found in the logs. A code your client does not know may appear in the future (adding one does not break v1): handle it by its HTTP status.
The most common codes
| Code | HTTP | What to do |
|---|---|---|
unauthorized | 401 | The credential is missing or expired. Check the Authorization header. |
forbidden | 403 | Your role does not allow the action in that organization. |
not_found | 404 | It does not exist or is not in your organization (deliberately indistinguishable). |
validation_error | 422 | The body or parameters do not match the contract; the message says which field. |
too_many_requests | 429 | Wait as long as Retry-After says. |
feature_disabled | 404 | The organization has that feature off (community, gamification or AI). |
organization_suspended | 403 | The organization is suspended: talk to its admin. |
plan_limit_reached | 403 | A plan limit has been reached (seats, courses, storage). |
dpa_required | 409 | Reserved: no longer returned (EduRails is only for people aged 18 or over). |
step_up_required | 403 | The action requires confirming your identity again. |
credential_read_only | 403 | The token or OAuth grant is read-only. |
conflict | 409 | The resource changed or already exists; read it again. |
service_unavailable | 503 | Temporary outage: retry after Retry-After. |
internal_error | 500 | Our fault: keep the request_id and let us know. |
The full list is in the ErrorCode schema of the reference.