Reference
Errors
Every error your app can receive, and what to do about each one.
Errors reach your app in four ways: as a page SSO shows the person, as parameters on your redirect URI, as a JSON body from a server-to-server call, or as a 401 from an endpoint that takes a bearer token.
Errors SSO shows the person
SSO never redirects a request it can't trust. These problems show an error page at sso.aldelo.com, and your app gets no callback:
- the
client_idis unknown, or the app is suspended; - the
redirect_uriisn't one of your registered URIs; response_typeisn'tcode, or PKCE is missing or doesn't useS256;scopeis empty or asks for a scope your app isn't registered for;stateis shorter than 8 bytes, ornonceis outside 8 to 512 bytes;prompt,max_ageoracr_valuesis malformed.
The page shows a reference code. Send it to developer@aldelo.com and we can find the exact reason; for example, a bad nonce is recorded as nonce_invalid.
The same applies to /logout: a missing, expired or foreign id_token_hint, or an unregistered post_logout_redirect_uri, shows an error page. If the person declines to share their details with your app, the sign-in also ends on an SSO page.
Errors returned to your app
Once SSO has checked your app and redirect URI, later problems come back to your redirect URI as error and error_description, with your state:
GET https://app.example.com/callback?error=access_denied&error_description=organization_access_denied&state=af0ifjsldkj-state
| error | error_description | Why | What to do |
|---|---|---|---|
login_required | The Authorization Server requires End-User authentication. | You sent prompt=none, and the person has no SSO session recent enough for your request. | Start an interactive sign-in. |
interaction_required | organization_selection_required | You sent prompt=none to a multi-organization app, and the person has to choose an organization. | Start an interactive sign-in. See Organizations. |
access_denied | organization_access_denied | The person isn't an active member of the organization your app needs, or of any organization your multi-organization app serves. | Ask them to get access from their organization's administrator. |
access_denied | strong authentication required; no second factor enrolled | You required urn:aldelo:sso:acr:mfa, and the person hasn't set up a second factor. | Ask them to add a passkey or an authenticator app to their Aldelo account. |
access_denied | strong authentication required; no second factor enrolled | You required urn:aldelo:sso:acr:pwd of a person who signed in by text message, and they haven't set up a second factor either. | Ask them to add a passkey or an authenticator app, or to sign in with a password instead. |
invalid_request | Names the value | An acr_values entry isn't supported. | Send only values listed in the discovery document's acr_values_supported. |
Match these error_description values as whole strings; don't parse them.
Token endpoint errors
POST /oauth2/token answers problems as JSON in the OAuth 2.0 shape, for example:
{
"error": "invalid_grant",
"error_description": "A description for developers."
}
| Status | error | Meaning |
|---|---|---|
400 | invalid_request | The body is malformed, or the code has expired. |
400 | invalid_grant | The code is unknown, already used or revoked, the PKCE verifier doesn't match, or the person's access changed since they signed in. |
400 | unsupported_grant_type | Only authorization_code is supported. |
401 | invalid_client | The client_id is unknown, or the client secret is wrong or missing. |
429 | (JSON envelope) | Too many requests: the endpoint allows 5 requests per 60 seconds per client and IP address. |
500 | server_error | The rate limiter itself failed. Rare; treat it like any other server error. |
503 | temporarily_unavailable | Try again shortly. |
Never retry a code exchange
A code works once. Using it a second time fails, and also revokes the access token the first exchange issued.
Bearer token errors
/userinfo and /api/v1/me/memberships answer every token problem the same way, whether the token is missing, malformed, expired or revoked, the app is suspended, or the app session has ended:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer error="invalid_token"
Content-Type: application/json;charset=UTF-8
{"error":"invalid_token"}
Sign the person in again to get a new token.
/oauth2/introspect and /oauth2/revoke both authenticate the caller the same way the token endpoint does: your own client credentials, a client secret, in the Authorization header or as form fields. A public app — a browser or mobile client, which has none — cannot call either.
Both share the same failure shapes: 401 on a client-authentication failure, 400 on a missing token parameter, 429 on the rate limit (5 requests per 60 seconds per client and IP address, the same for both), 500 on a server-side fault, and 503 temporarily_unavailable when the endpoint isn't wired. Beyond those, each endpoint never reports a token problem as an error: /oauth2/introspect answers {"active":false} for every unknown, malformed, revoked, expired, cross-owner, app-session-ended or suspended-client token, and /oauth2/revoke answers 200 for every token, active or not, whether or not anything was flipped.
JSON API errors
Endpoints under /api/v1, and the rate limits, use one JSON envelope:
{
"error": {
"code": "RATE_LIMITED",
"message": "too many requests",
"correlation_id": "req_00000000-0000-0000-0000-000000000000",
"timestamp": "2026-01-01T00:00:00Z"
}
}
Every response carries an X-Correlation-ID header. Send your own X-Correlation-ID (up to 128 bytes) to trace a request across your services and ours, and quote it when you contact us.