Documentation Index
Fetch the complete documentation index at: https://docs.busha.io/llms.txt
Use this file to discover all available pages before exploring further.
Error envelopes
Errors come in two shapes depending on which surface returned them. OAuth2 protocol endpoints (/oauth2/auth, /oauth2/token, /oauth2/revoke) return RFC 6749 §5.2 shape:
error_description is human-readable diagnostic text — do not surface it verbatim to end users.
OAuth2 errors
| Error | Surface | Cause | Fix |
|---|---|---|---|
invalid_request | All OAuth2 endpoints | Malformed request — missing parameter, bad encoding, wrong content type. | Validate your request shape against the Endpoints reference. |
invalid_client | /oauth2/token | Wrong client_id, wrong client_secret, or wrong authentication method. | Use HTTP Basic auth with URL-encoded client_id:client_secret. |
invalid_grant | /oauth2/token | Code reused, code expired (10 min window), PKCE mismatch, or refresh token already rotated (family revoked). | For codes: make your callback handler idempotent — deduplicate on state or the code itself. For refresh tokens: treat as a hard disconnect and prompt re-authorization. |
invalid_scope | /oauth2/auth | Scope not in the catalog, or not in your approved_scopes. | Compare your requested scopes against the catalog and your approved set on the dashboard. |
unauthorized_client | /oauth2/auth | Client status is pending, rejected, or suspended. | Check your app status on the dashboard. |
access_denied | Callback (/callback) | User clicked Cancel on the consent screen. | Render a “continue without connecting” path in your UI. |
redirect_uri does not match | OAuth2 endpoints | redirect_uri is not byte-identical to a registered URI. | Compare bytes against the dashboard value. Check trailing slash, scheme, port, path casing. |
Resource API errors
| HTTP status | Error name | Cause | Fix |
|---|---|---|---|
401 Unauthorized | — | Access token expired, revoked, or signature verification failed. | Refresh the access token. Force a JWKS refetch if the kid is unknown. |
403 Forbidden | insufficient_scope | The token does not include the scope required by this endpoint. | Inspect the scp claim in the access token. Re-authorize with the broader scope set. |
Common gotchas
invalid_grant on code exchange
Token exchange is not idempotent — authorization codes are single-use. If your callback handler runs twice (user double-clicks, browser retry), the second exchange returns invalid_grant. Deduplicate on the state value or the code itself in your callback handler.
invalid_grant on refresh
A refresh token can only be used once. If you replay a previously-used refresh token, Busha revokes the entire token family — all tokens for that user are immediately invalidated. Handle any invalid_grant on a refresh as a hard disconnect and send the user through the authorization flow again.
Access token still works after refresh token revocation
Revoking a refresh token prevents new tokens from being minted, but does not invalidate an already-issued access token. JWTs self-expire at exp (~1 hour). This is by design. If you need instant invalidation, layer a server-side allowlist on critical endpoints.
openid scope not shown on consent screen
openid and offline_access are protocol scopes that Busha hides from the consent UI. If you received an id_token and refresh_token, they were granted successfully.