/token, /introspect, /revoke) follow RFC 6749 §5.2; the authorization endpoint redirects with §4.1.2.1 errors. Their shape:
/api/public/v1/games/{game}/...) instead returns Flexslot’s canonical envelope ({ "error": "FLEXSLOT_*", "message", "details" }) and sends no WWW-Authenticate: Bearer header — see Errors from the resource server below. (Flexslot does not emit an error_uri field on any error.)
Most errors are programmer errors — a bad request, a misconfigured client. A few are user-driven (access_denied) and a few are signals of something seriously wrong (invalid_grant on a fresh code).
Errors from /authorize
When the user lands on the authorization endpoint with a broken request, Flexslot redirects them back to your redirect_uri with error=... in the query string — unless the redirect_uri itself is invalid, in which case Flexslot shows an error page instead (so we never redirect to a wrong location).
| Error code | HTTP | Meaning | Fix |
|---|---|---|---|
invalid_request | redirect | Required parameter missing or malformed | Check response_type, client_id, redirect_uri, code_challenge, code_challenge_method |
unauthorized_client | redirect | This client_id is not allowed to use response_type=code | Check the client’s grant_types in partner admin |
access_denied | redirect | The user clicked Deny | Show a friendly “you denied access” page |
unsupported_response_type | redirect | response_type is not code | Use response_type=code |
invalid_scope | redirect | Requested scope isn’t in the client’s allowed scopes | Add the scope to the client in partner admin |
server_error | redirect | Flexslot bug | Check status page; retry; report if persistent |
temporarily_unavailable | redirect | AS is overloaded or in maintenance | Back off and retry |
| (HTML error page) | 400 | client_id is unknown OR redirect_uri doesn’t match | Fix client_id; check exact-string match for redirect_uri |
Example error redirect
Errors from /token
The token endpoint returns JSON with appropriate HTTP status codes.
invalid_request — 400
The request is missing a required parameter, includes an unsupported parameter, or is malformed.
Common causes:
- Missing
grant_type - Missing
codeonauthorization_codegrant - Missing
refresh_tokenonrefresh_tokengrant - Missing
redirect_urionauthorization_codegrant - Wrong
Content-Type(must beapplication/x-www-form-urlencoded) redirect_uridoesn’t match the one used at/authorizebyte-for-byte
redirect_uri you sent on the /authorize request.
invalid_client — 401
Client authentication failed.
Common causes:
- Wrong
client_id - Wrong
client_secret - Client exists but is disabled in partner admin
- Trying to authenticate as a confidential client without HTTP Basic
- Trying to omit
client_idfrom a public client request
401 — it does not send a WWW-Authenticate header.
Fix: verify credentials in the partner admin. If you suspect the secret was rotated, generate a new one.
invalid_client is not the same as “wrong password”. It also fires on missing client_id, wrong authentication method, or a disabled client.invalid_grant — 400
The provided authorization grant (code or refresh token) is invalid, expired, revoked, or doesn’t match the redirect URI or PKCE verifier.
Common causes:
- Authorization code already used (codes are single-use)
- Authorization code expired (10 minutes)
code_verifierdoesn’t hash to the originalcode_challengeredirect_uridiffers from the original/authorizerequest- Refresh token expired (30 days idle)
- Refresh token already rotated (you replayed an old one — grant is now revoked)
unauthorized_client — 400
The authenticated client is not authorized to use the requested grant type.
Common causes:
- Client only has
authorization_codeconfigured, but the request usedrefresh_token - Client was changed to disable a grant type after issuing tokens
unsupported_grant_type — 400
The grant_type parameter is something Flexslot doesn’t support.
Supported values:
authorization_coderefresh_token
- Sending
password(forbidden by RFC 9700) - Sending
client_credentials(not supported — use a PAT) - Typo
invalid_scope — 400
A requested scope is unknown or not allowed for this client.
Example:
Errors from the resource server (/api/public/v1/...)
When a request to the API fails because of token or scope issues, the response is the canonical Flexslot envelope ({ "error": "FLEXSLOT_*", "message": "...", "details": { ... } }). The Bearer lane does not emit an RFC 6750 WWW-Authenticate: Bearer challenge — read the error code from the body. (The only resource-server response with a WWW-Authenticate header is the DPoP lane; see below.)
FLEXSLOT_OAUTH2_TOKEN_EXPIRED — 401
The access token is past its 1-hour TTL.
invalid_grant, redirect the user to re-authorize.
FLEXSLOT_OAUTH2_TOKEN_REVOKED — 401
The access token (or its grant) was revoked.
FLEXSLOT_AUTHENTICATION_FAILED — 401
The token is unknown/malformed, the user is inactive, the grant was revoked, or a DPoP-bound token was presented on the plain Bearer lane (downgrade defense — use the DPoP scheme instead).
FLEXSLOT_INSUFFICIENT_SCOPE — 403
The token is valid but doesn’t have the scope this endpoint requires. The details object names the required and granted scopes.
decks:write does not satisfy a decks:read requirement. Fix: start a new authorization flow with the additional scope. Don’t retry the same call with the same token — it will fail every time. See Scopes → Incremental authorization.
Missing Authorization header — 401
Authorization: Bearer <access_token>. The header name is case-insensitive but the scheme (Bearer) is case-sensitive in practice on some proxies — use the canonical capitalization.
DPoP lane — WWW-Authenticate: DPoP error="invalid_dpop_proof" — 401
The only resource-server response carrying a WWW-Authenticate header. Emitted on the Authorization: DPoP … scheme when the proof is missing/malformed/stale, the ath doesn’t match, the jti was replayed, or the proof’s JWK thumbprint doesn’t match the token’s bound key. Body: { "error": "FLEXSLOT_AUTHENTICATION_FAILED", ... }. See DPoP → errors.
Errors from /revoke
RFC 7009 deliberately defines revocation to return 200 for both successful revocation and revocation of an already-invalid token. This prevents using /revoke as an oracle for token validity.
The only errors you’ll see:
| Error | HTTP | Meaning |
|---|---|---|
invalid_client | 401 | Bad client credentials |
invalid_request | 400 | Missing token parameter |
Errors from /introspect
RFC 7662. When the introspection succeeds (even for an invalid token), you get 200 with {"active": false} or {"active": true, ...}. You only see HTTP errors for client-auth failures.
Debugging checklist
When a flow fails and you’re not sure why:Diff your /authorize and /token redirect_uri
Most
invalid_request cases are a trailing slash, scheme mismatch, or stray query string.Confirm PKCE state
code_verifier from the same session as the code_challenge. Logging the first/last 4 chars of both (never the full value) on dev makes this obvious.Check parallel refreshes
Two requests refreshing the same token at once = one gets revoked. Add a per-user mutex.