Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.flexslot.gg/llms.txt

Use this file to discover all available pages before exploring further.

Every error from the OAuth endpoints follows RFC 6749 §5.2 for the token endpoint and §4.1.2.1 for the authorization endpoint. The shape is consistent:
{
  "error": "invalid_grant",
  "error_description": "The authorization code is expired or has already been used.",
  "error_uri": "https://flexslot.gg/docs/oauth/errors#invalid_grant"
}
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 codeHTTPMeaningFix
invalid_requestredirectRequired parameter missing or malformedCheck response_type, client_id, redirect_uri, code_challenge, code_challenge_method
unauthorized_clientredirectThis client_id is not allowed to use response_type=codeCheck the client’s grant_types in partner admin
access_deniedredirectThe user clicked DenyShow a friendly “you denied access” page
unsupported_response_typeredirectresponse_type is not codeUse response_type=code
invalid_scoperedirectRequested scope isn’t in the client’s allowed scopesAdd the scope to the client in partner admin
server_errorredirectFlexslot bugCheck status page; retry; report if persistent
temporarily_unavailableredirectAS is overloaded or in maintenanceBack off and retry
(HTML error page)400client_id is unknown OR redirect_uri doesn’t matchFix client_id; check exact-string match for redirect_uri

Example error redirect

GET https://your.app/oauth/callback?
  error=invalid_scope&
  error_description=Scope%20cards%3Awrite%20is%20not%20a%20valid%20scope&
  state=4f8a9c1e2b3d
Always include state validation before you act on an error. An attacker could send a crafted callback with an error=... they chose. If state doesn’t match, abort the whole flow.

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 code on authorization_code grant
  • Missing refresh_token on refresh_token grant
  • Missing redirect_uri on authorization_code grant
  • Wrong Content-Type (must be application/x-www-form-urlencoded)
  • redirect_uri doesn’t match the one used at /authorize byte-for-byte
Example:
{
  "error": "invalid_request",
  "error_description": "redirect_uri does not match the one used in the authorization request"
}
Fix: echo back the exact 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_id from a public client request
Example:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="flexslot"
Content-Type: application/json

{
  "error": "invalid_client",
  "error_description": "Client authentication failed"
}
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 (60 seconds)
  • code_verifier doesn’t hash to the original code_challenge
  • redirect_uri differs from the original /authorize request
  • Refresh token expired (30 days idle)
  • Refresh token already rotated (you replayed an old one — grant is now revoked)
Example:
{
  "error": "invalid_grant",
  "error_description": "code_verifier does not match the stored code_challenge"
}
Fix: start a new authorization flow. If this happens on refresh, the grant was revoked (likely refresh token replay) — the user must re-consent.
If you see invalid_grant repeatedly on refresh, you have a refresh token rotation bug. Two parallel refreshes both succeed, both get new tokens, but only one is stored — the next refresh uses a stale token and revokes the grant. Serialize refreshes.

unauthorized_client — 400

The authenticated client is not authorized to use the requested grant type. Common causes:
  • Client only has authorization_code configured, but the request used refresh_token
  • Client was changed to disable a grant type after issuing tokens
Fix: add the grant type to the client’s allowed grants in partner admin.

unsupported_grant_type — 400

The grant_type parameter is something Flexslot doesn’t support. Supported values:
  • authorization_code
  • refresh_token
Common causes:
  • Sending password (forbidden by RFC 9700)
  • Sending client_credentials (not supported — use a PAT)
  • Typo
Fix: use one of the two supported grant types.

invalid_scope — 400

A requested scope is unknown or not allowed for this client. Example:
{
  "error": "invalid_scope",
  "error_description": "Scope 'admin:write' is not a valid scope"
}
Fix: check the scope catalog and the client’s allowed scopes in partner admin.

Errors from the resource server (/api/public/v1/...)

When a request to the API fails because of token issues, the response uses RFC 6750 bearer token error conventions.

invalid_token — 401

The token is malformed, expired, or revoked.
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="flexslot",
  error="invalid_token",
  error_description="The access token expired"
Fix: refresh the access token. If the refresh also fails with invalid_grant, redirect the user to re-authorize.

insufficient_scope — 403

The token is valid but doesn’t have the scope this endpoint requires.
HTTP/1.1 403 Forbidden
WWW-Authenticate: Bearer realm="flexslot",
  error="insufficient_scope",
  error_description="Requires scope: decks:write",
  scope="decks:write"
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 or malformed Authorization header — 401

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="flexslot"
No error code in the body because per RFC 6750, when there’s no token at all there’s no error to report — just the challenge. Fix: add 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.

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:
ErrorHTTPMeaning
invalid_client401Bad client credentials
invalid_request400Missing 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.
{
  "active": false
}
{
  "active": true,
  "scope": "decks:read sideboards:read",
  "client_id": "cli_01HX2K…",
  "username": "user_01HX2K…",
  "exp": 1717180800,
  "iat": 1717177200
}

Debugging checklist

When a flow fails and you’re not sure why:
1

Check the error_description

It’s always populated and is usually specific. Read it before assuming.
2

Diff your /authorize and /token redirect_uri

Most invalid_request cases are a trailing slash, scheme mismatch, or stray query string.
3

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.
4

Check the clock

A skewed server clock can make a fresh code look expired. Run NTP.
5

Check parallel refreshes

Two requests refreshing the same token at once = one gets revoked. Add a per-user mutex.
6

Call /introspect on a token that's misbehaving

Confirms whether the token is active, what scopes it has, and when it expires.
Never log token values, even truncated, in production. In development, log the first and last 4 characters only — enough to tell two tokens apart, not enough to use one.