Obexal Docs

Docs/Reference/Errors and rate limits

Errors and limits

The error envelopes of each API surface, the common error codes, rate limits, account lockout and the platform caps.

Obexal has three API surfaces, and each uses the error convention its protocol expects. This page lists the shapes, the stable codes, and every enforced limit.

Error formats

JSON API (everything under /v1/): a single envelope on every non-2xx response. code is a stable machine string; message is human-readable and may change.

{"error": {"code": "rate_limited", "message": "too many requests"}}

OAuth endpoints (/oauth/*): the RFC 6749 shape, with snake_case fields at the top level.

{"error": "invalid_grant", "error_description": "code invalide, expiré ou déjà utilisé"}

On /oauth/authorize, errors are redirected to the client as error, error_description and state query parameters once the redirect_uri has been validated; before that, the response is a direct 400 (never an open redirect). A failed client_secret_basic authentication returns 401 with a WWW-Authenticate: Basic header. /oauth/userinfo returns 401 with a WWW-Authenticate: Bearer error="invalid_token" header (its body uses the JSON envelope).

SCIM (/scim/v2/*): the standard SCIM error body, see the SCIM reference.

{"schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"], "status": "404", "detail": "utilisateur introuvable"}

Common API error codes

CodeHTTP statusMeaning
invalid_request400Malformed body or invalid parameters
unauthenticated401Session missing, expired or revoked
invalid_credentials401Wrong e-mail or password
unauthorized401Admin API token missing, invalid, expired or revoked
forbidden403Permission missing (RBAC deny-by-default, or outside the token's scopes)
csrf_failed403Missing or mismatched X-CSRF-Token header on a cookie-authenticated mutation
step_up_required403Sensitive action requires a fresh second-factor check (POST /v1/mfa/step-up)
mfa_enrollment_required403The tenant requires MFA and no factor is enrolled (strict mode)
not_found404Resource absent from your tenant
conflict409Resource already exists
payload_too_large413Request body over the limit
rate_limited429Too many attempts, retry later
account_locked429Account locked after repeated sign-in failures
internal_error500Unexpected server error

Feature-specific codes also exist (for example slug_taken, email_taken, invalid_invite, password_expired): treat the HTTP status as the class and the code as the precise reason.

OAuth error codes

CodeTypical cause
invalid_requestMissing or malformed parameter (missing code, missing code_verifier, unsupported token type URN)
invalid_clientUnknown client, wrong or expired client_secret (401 when presented via Basic)
invalid_grantCode or refresh token invalid, expired, replayed or bound to another client; agent disabled or expired; suspended tenant
unauthorized_clientGrant type not enabled for this client
unsupported_grant_typeUnknown grant_type value
unsupported_response_typeAnything other than code
invalid_scopeRequested scope outside the allowed set (client scopes, subject scopes, agent ceiling or user authorization)
access_deniedUser or tenant boundary refused the request
login_required, consent_requiredInteraction needed but prompt=none forbids it
invalid_targetresource is not an absolute URI or not in the agent's allowed audiences (RFC 8693/8707)
invalid_dpop_proofMissing, malformed or replayed DPoP proof
server_errorUnexpected server error

Rate limits

Sensitive flows are throttled with fixed windows, keyed per e-mail and per client IP independently. Two families exist; their default values are in the configuration reference:

FamilyCovered flows
Sign-in (RATE_LIMIT_LOGIN_*)Password sign-in, passwordless, MFA challenges and resends, passkey sign-in
Account (RATE_LIMIT_SIGNUP_*)Sign-up, organization creation, password reset requests

E-mail verification, e-mail change, invitation and social sign-in flows are throttled by the same limiters. Exceeding a limit returns 429 rate_limited and the event is recorded in the audit log.

Account lockout

Independently of IP-based limits, repeated password failures lock the account itself, which resists IP rotation (LOGIN_LOCKOUT_MAX, LOGIN_LOCKOUT_WINDOW, LOGIN_LOCKOUT_DURATION); the default values are in the configuration reference. The response is 429 account_locked. An administrator can lift the lock with POST /v1/admin/users/unlock.

One-time codes have their own caps: e-mail OTPs allow 5 attempts and expire after 10 minutes; MFA challenges allow 5 attempts and expire after 5 minutes (defaults).

Platform caps

LimitValue
Request body (JSON APIs)1 MiB
List pagination (?limit)Default 100, maximum 500
Audit export (GET /v1/admin/audit/export)10000 events per call; the response sets truncated: true beyond
Audit search query (?q)200 characters
Access policy versions keptThe 50 most recent
Policy simulation sampleSign-ins from the last 30 days, up to 1000 distinct IPs
Countries per conditional access rule250
SCIM list page200 resources
Note

These caps are enforced server side; clients should paginate rather than raise them.