OAuth 2.0 PKCE flow, token exchange, session management, DCR, resource indicators

Base URL: https://api.fast.io/current/ Request format: application/x-www-form-urlencoded Response format: JSON

Overview

Fastio implements OAuth 2.0 with PKCE (Proof Key for Code Exchange) for secure authorization without passing user credentials through the client application. This is the recommended auth method for desktop apps, mobile apps, and MCP-connected agents.

Key characteristics:

Endpoint Summary

MethodPathAuthDescription
GET/.well-known/oauth-authorization-server/NoneRFC 8414 authorization server metadata
GET/.well-known/oauth-protected-resource/NoneRFC 9728 protected resource metadata
POST/current/oauth/register/NoneRFC 7591 dynamic client registration
PUT/current/oauth/register/Registration access token (Bearer)RFC 7592 update client registration
GET/current/oauth/authorize/NoneInitiate auth flow — 302 redirect to login page (or JSON with response_format=json)
POST/current/oauth/authorize/Session (JWT)Complete authorization, issue code
GET/current/oauth/authorize/info/NoneGet client info for consent screen
POST/current/oauth/token/NoneExchange code for tokens, or refresh tokens
POST/current/oauth/revoke/NoneRevoke a refresh token
GET/current/oauth/sessions/BearerList all active sessions
GET/current/oauth/sessions/{id}/BearerGet session details
PATCH/current/oauth/sessions/{id}/BearerUpdate session display names, or narrow its scopes
DELETE/current/oauth/sessions/{id}/BearerRevoke a specific session
DELETE/current/oauth/sessions/BearerRevoke all sessions
GET/current/auth/scopes/BearerToken scope introspection

Metadata Discovery

GET /.well-known/oauth-authorization-server/

RFC 8414 Authorization Server Metadata. Returns server configuration for automated client setup.

GET /.well-known/oauth-authorization-server/

Auth: None

curl -X GET "https://api.fast.io/.well-known/oauth-authorization-server/"

Response (200 OK):

{
  "issuer": "https://go.fast.io",
  "authorization_endpoint": "https://go.fast.io/api/current/oauth/authorize/",
  "token_endpoint": "https://go.fast.io/api/current/oauth/token/",
  "revocation_endpoint": "https://go.fast.io/api/current/oauth/revoke/",
  "registration_endpoint": "https://go.fast.io/api/current/oauth/register/",
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "token_endpoint_auth_methods_supported": ["none"],
  "code_challenge_methods_supported": ["S256"],
  "resource_indicators_supported": true,
  "scopes_supported": ["user", "org", "workspace", "all_orgs", "all_workspaces", "all_shares", "all_sign_envelopes"],
  "access_modes_supported": ["r", "rw", "rwa"],
  "service_documentation": "https://go.fast.io/api/current/llms/"
}

Response Fields:

FieldTypeDescription
issuerstringAuthorization server issuer identifier URL
authorization_endpointstringURL of the authorization endpoint
token_endpointstringURL of the token endpoint
revocation_endpointstringURL of the token revocation endpoint
registration_endpointstringURL of the dynamic client registration endpoint
response_types_supportedarraySupported response types (code only)
grant_types_supportedarraySupported grant types (authorization_code, refresh_token)
token_endpoint_auth_methods_supportedarraySupported auth methods (none — public clients only)
code_challenge_methods_supportedarraySupported PKCE methods (S256 only)
resource_indicators_supportedbooleanRFC 8707 support (true)
scopes_supportedarrayOAuth scope type selectors supported by the authorization server (see the scope-type values table under GET /current/oauth/authorize/)
access_modes_supportedarrayAccess modes the authorization server accepts on access_mode: ["r","rw","rwa"]
service_documentationstringURL to service documentation

Clients validating access_modes_supported against {r, rw} must accept rwa. The list now carries a third mode — read, write and administer — so a client that rejects unknown values will fail against it. scopes_supported is unchanged (one of its values, all_sign_envelopes, is retired and must not be requested — see Retired scopes below): userdetails is an entity type inside a credential's scope list, never a scope type, and is never advertised here.

Error Responses:

Reading the error tables: the four-digit 16xx/17xx values below are HTTP-status classes, not error.code. The error.code a client actually receives is assigned per endpoint, so use the HTTP status as the gate and a documented error.code — five or six digits, plus the 9661-9669 family — only as a refinement. A 16xx value identifies the status class — useful for telling which kind of failure occurred — but comparing one against error.code will never match. Five- and six-digit codes (and the 9661-9669 family) are real error.code values. If you widen a check from a specific code to a status, widen what you assert with it — a status covers failures the narrower code did not, so a message written for that one code becomes a confident falsehood on the rest.

ScenarioHTTP StatusError
Wrong HTTP method40510003

GET /.well-known/oauth-protected-resource/

RFC 9728 Protected Resource Metadata. Returns resource server configuration.

GET /.well-known/oauth-protected-resource/

Auth: None

curl -X GET "https://api.fast.io/.well-known/oauth-protected-resource/"

Response (200 OK):

{
  "resource": "https://mcp.fast.io/mcp",
  "authorization_servers": ["https://go.fast.io"],
  "bearer_methods_supported": ["header"],
  "scopes_supported": ["user", "org", "workspace", "all_orgs", "all_workspaces", "all_shares", "all_sign_envelopes"]
}

Response Fields:

FieldTypeDescription
resourcestringProtected resource identifier URL
authorization_serversarrayAuthorization server issuer URLs that can issue tokens for this resource
bearer_methods_supportedarrayMethods for presenting bearer tokens (header — Authorization header only)
scopes_supportedarrayOAuth scopes supported by this resource

Error Responses:

ScenarioHTTP StatusError
Wrong HTTP method40510003

Dynamic Client Registration

POST /current/oauth/register/

RFC 7591 Dynamic Client Registration. Register a new OAuth client programmatically.

POST /current/oauth/register/

Auth: None · Content-Type: application/json or application/x-www-form-urlencoded

Request Parameters:

ParameterTypeRequiredDefaultDescription
client_namestringNo"Unknown Client"Human-readable client name (max 128 characters)
redirect_urisJSON arrayYesAllowed redirect URIs. 1–10 URIs. HTTPS required (localhost/127.0.0.1 exempt). No fragment (#) components.
token_endpoint_auth_methodstringNo"none"Auth method (only public clients supported)
grant_typesJSON arrayNo["authorization_code", "refresh_token"]Requested grant types
response_typesJSON arrayNo["code"]Requested response types
curl -X POST "https://api.fast.io/current/oauth/register/" \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "My MCP Client",
    "redirect_uris": ["http://localhost:3000/callback", "http://127.0.0.1:3000/callback"]
  }'

Response (200 OK):

{
  "result": true,
  "client_id": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
  "client_name": "My MCP Client",
  "redirect_uris": [
    "http://localhost:3000/callback",
    "http://127.0.0.1:3000/callback"
  ],
  "token_endpoint_auth_method": "none",
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "registration_access_token": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
  "registration_client_uri": "https://api.fast.io/current/oauth/register/"
}

Response Fields:

FieldTypeDescription
resultbooleantrue on success
response.client_idstringAssigned client identifier for OAuth flows (32-character lowercase-hex string, no prefix)
response.client_namestringRegistered client name
response.redirect_urisarrayRegistered redirect URIs
response.token_endpoint_auth_methodstringToken endpoint auth method ("none")
response.grant_typesarrayGranted grant types
response.response_typesarrayGranted response types
response.registration_access_tokenstringOne-time token for managing registration via PUT (64-character lowercase-hex string, no prefix). Shown once only — store securely. Server stores only a SHA-256 hash.
response.registration_client_uristringURI for managing this client registration

Error Responses (RFC 7591 format — bare JSON, no platform envelope):

Error CodeHTTP StatusDescription
invalid_client_metadata400client_name exceeds 128 characters
invalid_client_metadata400redirect_uris is not a valid JSON array
invalid_client_metadata400token_endpoint_auth_method is not "none"
invalid_redirect_uri400redirect_uris must contain 1–10 entries
invalid_redirect_uri400Redirect URI is not a valid URL
invalid_redirect_uri400Redirect URI contains a fragment (#)
invalid_redirect_uri400Redirect URI must use HTTPS (except localhost)
invalid_request400redirect_uris is missing
server_error500Failed to register client

PUT /current/oauth/register/

RFC 7592 Dynamic Client Registration Management. Update an existing client registration. Requires the registration_access_token from the POST registration response. Only clients whose redirect URIs all point to localhost, 127.0.0.1, [::1], or ::1 may self-update.

PUT /current/oauth/register/

Auth: Registration access token (Bearer) · Content-Type: application/json or application/x-www-form-urlencoded

Request Parameters:

ParameterTypeRequiredDescription
client_idstringYesMust match a registered client
client_namestringNoUpdated client name (max 128 characters)
redirect_urisJSON arrayNoUpdated redirect URIs (full replacement, same validation as POST)

At least one of client_name or redirect_uris must be provided.

curl -X PUT "https://api.fast.io/current/oauth/register/" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" \
  -d '{
    "client_id": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
    "client_name": "My Updated MCP Client",
    "redirect_uris": ["http://localhost:8080/callback"]
  }'

Response (200 OK):

{
  "result": true,
  "client_id": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
  "client_name": "My Updated MCP Client",
  "redirect_uris": ["http://localhost:8080/callback"],
  "token_endpoint_auth_method": "none",
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"]
}

Response Fields:

FieldTypeDescription
resultbooleantrue on success
response.client_idstringClient identifier (unchanged)
response.client_namestringUpdated client name
response.redirect_urisarrayUpdated redirect URIs
response.token_endpoint_auth_methodstringAuth method (unchanged, always "none")
response.grant_typesarrayGrant types (unchanged)
response.response_typesarrayResponse types (unchanged)

Error Responses (RFC 7591 format — bare JSON):

Error CodeHTTP StatusDescription
invalid_request400client_id missing
invalid_request400Neither client_name nor redirect_uris provided
invalid_request400Only localhost clients can self-update
invalid_request401Invalid or missing registration access token
invalid_client404Client not found or disabled
invalid_client_metadata400Invalid client metadata
invalid_redirect_uri400Invalid redirect URIs (same rules as POST)
server_error500Failed to update registration

The grant_types, response_types, and token_endpoint_auth_method fields cannot be changed via self-update. If redirect_uris is provided, it fully replaces the existing set. If only client_name is provided, existing redirect URIs are preserved.

Client ID Metadata Document (CIMD)

CIMD allows OAuth clients to use an HTTPS URL as their client_id instead of pre-registering or using Dynamic Client Registration. The authorization server fetches metadata from the URL to get client information on-the-fly. This is the MCP specification's preferred client registration method.

How It Works

  1. Detection: If the client_id parameter starts with https://, the server treats it as a CIMD URL.
  2. Fetch: The server fetches the JSON metadata document from the CIMD URL.
  3. Validation: The document must contain:
    • client_id matching the fetched URL exactly
    • redirect_uris array containing the requested redirect URI
    • grant_types including authorization_code
    • response_types including code
    • token_endpoint_auth_method set to none
  4. Caching: Validated documents are cached for 1 hour to avoid repeated fetches.
  5. Flow: The CIMD URL is used as the client_id throughout the authorization flow (authorize, token exchange, refresh).

CIMD Document Format

The metadata document is a JSON file served at an HTTPS URL with Content-Type: application/json:

{
  "client_id": "https://example.com/oauth/client-metadata",
  "client_name": "Example App",
  "redirect_uris": ["http://localhost:8080/callback"],
  "grant_types": ["authorization_code"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "none"
}

Optional fields: client_uri (URL to the client's home page), logo_uri (URL to the client's logo image).

Security Constraints

ConstraintValue
ProtocolHTTPS only (HTTP URLs rejected)
Fetch timeout5 seconds
Max document size10 KB
Cross-domain redirectsRejected
Cache duration1 hour

Using CIMD in the Authorization Flow

Use the CIMD URL directly as the client_id parameter:

GET /current/oauth/authorize/?client_id=https://example.com/oauth/client-metadata&redirect_uri=http://localhost:8080/callback&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256&state=xyz123

The token endpoint also uses the CIMD URL as client_id — it matches by string comparison against the value stored during authorization.

Error Scenarios

ScenarioError
CIMD URL is not HTTPS1605 (Invalid Input)
Cannot fetch the document (timeout, DNS failure, non-200 response)1605 (Invalid Input)
Document is not valid JSON1605 (Invalid Input)
client_id in document does not match the URL1605 (Invalid Input)
Required fields missing or invalid1605 (Invalid Input)
redirect_uri not found in document's redirect_uris1605 (Invalid Input)

Complete PKCE Flow

Step 0: Discover Server Configuration (Optional)

Fetch server metadata for automated setup:

1. CLIENT -> API: Discover authorization server
   GET /.well-known/oauth-authorization-server/
   -> Returns endpoints, supported grant types, PKCE methods

2. CLIENT -> API: Discover protected resource (if needed)
   GET /.well-known/oauth-protected-resource/
   -> Returns resource URL and authorization servers

Step 0b: Register Client (If Needed)

If the client does not have a registered client_id, there are two options:

Option A: Use a CIMD URL as client_id — If the client publishes a metadata document at an HTTPS URL, use that URL directly as the client_id. No registration step needed.

Option B: Use Dynamic Client Registration:

CLIENT -> API: Register client
POST /current/oauth/register/
Content-Type: application/json

{"client_name": "My Agent", "redirect_uris": ["http://localhost:8080/callback"]}

-> Returns client_id, redirect_uris, registration_access_token, etc.

Step 1: Generate PKCE Parameters (Client-Side)

Before initiating the flow, generate the PKCE code verifier and challenge:

code_verifier = random_string(43-128 characters, URL-safe: [A-Za-z0-9-._~])
code_challenge = base64url_encode(sha256(code_verifier))
code_challenge_method = "S256"

The code_challenge will always be exactly 43 characters.

Step 2: Initiate Authorization

GET /current/oauth/authorize/

Initiates the authorization flow. By default, issues a 302 Found redirect to the login/consent page. This is the browser-facing entry point that MCP clients open in the user's browser.

GET /current/oauth/authorize/

Auth: None

JSON mode: Add response_format=json to receive a JSON response instead of a redirect (for programmatic callers).

Query Parameters:

ParameterTypeRequiredDefaultDescription
client_idstringYesRegistered OAuth client ID, or an HTTPS URL pointing to a CIMD
redirect_uristringYesMust match a registered URI for the client
response_typestringYesMust be "code"
code_challengestringYesExactly 43 characters (BASE64URL-encoded SHA-256)
code_challenge_methodstringYesMust be "S256"
statestringYesOpaque value for CSRF protection, returned unchanged in callback
scopestringNo"user"Scope type selector (see scope values table)
access_modestringNo"r", "rw" or "rwa". The ceiling for the whole authorization — consent may narrow it, never widen it. An empty value is treated as omitted; any other non-empty value returns 406 180242
account_settingsstringNo"0""1" to allow account-settings access (userdetails:*:rw) to be granted at consent, "0", an empty value, or omission for off. Any other value — including "true" and "false" — returns 406 117053
resourcestringNoRFC 8707 resource indicator (e.g., https://mcp.fast.io/mcp)
agent_namestringNoDisplay name of the requesting agent (max 128 characters)
response_formatstringNoSet to "json" for JSON response instead of 302 redirect

Scope type values (scope parameter):

ValueBehavior
userFull access (default, backward compatible with v1.0 JWT)
orgUser picks specific organizations
workspaceUser picks specific workspaces
all_orgsWildcard access to all user's organizations
all_workspacesWildcard access to all user's workspaces
all_sharesAll shares the user is a member of

These six are the complete offered set. Request exactly one of these named values, or omit scope for the default user; a scope outside this set is not a valid access request — the one exception is the retired all_sign_envelopes, which is still accepted and follows wildcard defaulting for compatibility but must not be requested (see Retired scopes below).

access_mode and account_settings are ceilings recorded with the authorization request. They are set here, on the initiate request, and the consent step (POST /current/oauth/authorize/) can only narrow them. access_mode bounds the access mode of every granted scope; account_settings decides whether account-settings access may be granted at all. A client that never sends them cannot obtain administration (rwa) or account settings through OAuth — administration is opt-in on a request the client made explicitly, and the consent screen never offers it unprompted.

Retired scopes — all_workflows and all_sign_envelopes: no longer offered. The Workflows feature has been removed, so all_workflows is deliberately omitted from scopes_supported and must not be requested. The authorization server retains it only as a fail-closed tombstone, so a pre-existing token that still carries it resolves to nothing rather than erroring — it is not a capability you can request. The E-Signature feature has likewise been removed, and although all_sign_envelopes still appears in scopes_supported for compatibility, it must not be requested; a credential that still holds it reports a sign_envelope entity type, which no longer maps to any offered surface.

# Standard browser flow (302 redirect)
curl -v "https://api.fast.io/current/oauth/authorize/?client_id=my-app&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fcallback&response_type=code&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256&state=abc123xyz"

# JSON mode (programmatic)
curl "https://api.fast.io/current/oauth/authorize/?client_id=my-app&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fcallback&response_type=code&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256&state=abc123xyz&response_format=json"

Response (default — 302 Found):

Location: https://go.fast.io/connect?auth_request_id=a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2

Response (response_format=json — 200 OK):

{
  "result": true,
  "auth_request_id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
  "client_name": "My App",
  "scope": "user"
}

Response Fields (JSON mode):

FieldTypeDescription
resultbooleantrue on success
response.auth_request_idstring64-character hex identifier for this authorization request (valid for 10 minutes)
response.client_namestringHuman-readable name of the OAuth client application
response.scopestringThe granted scope
response.agent_namestringAgent display name (present if agent_name was provided)

Error Responses (standard platform envelope):

ScenarioError CodeHTTP Status
client_id missing1605 (Invalid Input)406
redirect_uri missing1605 (Invalid Input)406
code_challenge missing1605 (Invalid Input)406
code_challenge_method missing1605 (Invalid Input)406
code_challenge_method not "S256"1605 (Invalid Input)406
code_challenge not 43 characters1605 (Invalid Input)406
state missing1605 (Invalid Input)406
client_id not found1605 (Invalid Input)406
Client is disabled1605 (Invalid Input)406
redirect_uri mismatch1605 (Invalid Input)406
Invalid resource indicator1605 (Invalid Input)406
access_mode non-empty and not "r", "rw" or "rwa"180242406
account_settings non-empty and not "1" or "0"117053406
Internal storage failure1654 (Internal Error)500

POST /current/oauth/authorize/

Complete authorization after user login. Issues the authorization code. Called by the web application (not the client directly) with the user's active session.

POST /current/oauth/authorize/

Auth: Required (JWT — website session)

Request Parameters:

ParameterTypeRequiredDefaultDescription
auth_request_idstringYes64-character hex authorization request ID from the GET request
scopesstringConditionalJSON array of entity IDs or scope strings (e.g., "[12345, 67890]" or '["org:12345:rw"]'). Must be a non-empty JSON list when supplied, and is required for scope=org and scope=workspace
access_modestringNothe value stored at initiate"r", "rw" or "rwa" — may only narrow the initiate value, never widen it
account_settingsstringNo"0""1" appends userdetails:*:rw server-side, and only when the authorization was initiated with account_settings=1
agent_namestringNoOverride the agent display name from the GET request (max 128 characters)

Consent rules — scopes, access mode and account settings:

curl -X POST "https://api.fast.io/current/oauth/authorize/" \
  -H "Authorization: Bearer {jwt_token}" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "auth_request_id=a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"

Response (200 OK):

{
  "result": true,
  "redirect_uri": "http://localhost:8080/callback?code=abc123def456abc123def456abc123def456abc123def456abc123def456abc123de&state=abc123xyz",
  "redirect_mode": "redirect"
}

Response Fields:

FieldTypeDescription
resultbooleantrue on success
response.redirect_uristringFull redirect URI with code (64 hex chars, valid 5 min, single-use) and state query parameters
response.redirect_modestring"redirect" for HTTP/HTTPS redirect URIs, "button" for custom scheme URIs
response.button_labelstring or nullCustom button label for "button" redirect mode (if configured)

Error Responses:

ScenarioError CodeHTTP Status
User not authenticated1650 (Authentication Invalid)401
auth_request_id missing1605 (Invalid Input)406
Request expired or not found1609 (Not Found)404
Invalid access_mode1605 (Invalid Input)406
Scope validation failed1605 (Invalid Input)406
Unrecognised access_mode value161579406
access_mode broader than the initiate value, a client-supplied userdetails scope, or account_settings=1 without it at initiate10768 (access_mode_exceeds_initiate)403
scopes not a non-empty JSON list168998 / 128552406
scopes omitted for scope=org or scope=workspace121987406
Consent made with a narrowed (non-account-level) credential10175403
Granted scopes exceed the consenting credential10768 (scope_exceeds_issuer)403
More than 20 scopes after the server-side append157349406
Internal processing error1654 (Internal Error)500

GET /current/oauth/authorize/info/

Validate an authorization request and return client information (app name, requested scope). Used by the web frontend to display a consent screen before the user confirms.

GET /current/oauth/authorize/info/

Auth: None

Query Parameters:

ParameterTypeRequiredDescription
auth_request_idstringYesThe authorization request ID to validate
curl "https://api.fast.io/current/oauth/authorize/info/?auth_request_id=a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"

Response (valid request — 200 OK):

{
  "result": true,
  "valid": true,
  "client_name": "My App",
  "scope": "user",
  "agent_name": "My MCP Agent",
  "redirect_mode": "redirect",
  "redirect_uri": "http://localhost:8080/callback",
  "resource": "https://mcp.fast.io/mcp",
  "access_mode": "rw",
  "account_settings": false
}

resource is the RFC 8707 resource indicator the client supplied at initiate (or null), access_mode is the requested access mode ("r", "rw" or "rwa", or null when the client did not supply one), and account_settings is a boolean that is always present. All three are returned server-authoritatively from the stored authorization request, so consent screens can render them without trusting URL parameters.

Show the account-settings toggle only when account_settings is true, and default it to off. When it is false the authorization was not initiated for account settings, and consent cannot grant them.

Response (invalid or expired — 200 OK):

{
  "result": true,
  "valid": false
}

Response Fields:

FieldTypeDescription
resultbooleanAlways true
response.validbooleantrue if the auth request is valid and client is active
response.client_namestringClient name (only when valid is true)
response.scopestringRequested scope (only when valid is true)
response.agent_namestringAgent display name (only when set and valid is true)
response.redirect_modestring"redirect" or "button" (only when valid is true)
response.button_labelstringCustom button label (only when configured and valid is true)
response.redirect_uristringClient redirect URI (only when valid is true)
response.resourcestring or nullThe RFC 8707 resource indicator recorded at initiate, or null when the client supplied none. Always present when valid is true
response.access_modestring or nullRequested access mode — "r" (read-only), "rw" (read-write) or "rwa" (read-write-administer) — or null when the client supplied none. It is the ceiling for consent. Always present when valid is true
response.account_settingsbooleanWhether the authorization was initiated with account_settings=1, i.e. whether account-settings access (userdetails:*:rw) may be granted at consent. Always present when valid is true; render the toggle only when it is true, defaulted to off

This endpoint never returns an error for invalid auth_request_id. It returns valid: false instead, preventing information leakage about authorization request existence.

Step 3: User Approves in Browser

The user opens the authorization URL, signs in (supports SSO), and approves access. The browser either:

Step 4: Exchange Code for Tokens

POST /current/oauth/token/

Exchange an authorization code for access and refresh tokens, or refresh an existing access token.

POST /current/oauth/token/

Auth: None · Content-Type: application/x-www-form-urlencoded

Important: This endpoint returns bare JSON responses (RFC 6749 format, no platform envelope) for compatibility with standard OAuth clients, including MCP hosts.

Authorization Code Exchange

Request Parameters:

ParameterTypeRequiredDescription
grant_typestringYesMust be "authorization_code"
codestringYes64-character hex authorization code from the callback
code_verifierstringYesOriginal PKCE code verifier (43–128 characters, [A-Za-z0-9-._~])
client_idstringYesMust match original request
redirect_uristringYesMust match original request
resourcestringNoRFC 8707 resource indicator URL (must match authorize request)
device_namestringNoHuman-readable device name for session tracking
device_typestringNoDevice category for session tracking
curl -X POST "https://api.fast.io/current/oauth/token/" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code&code=abc123def456abc123def456abc123def456abc123def456abc123def456abc123de&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk&client_id=my-app&redirect_uri=http://localhost:8080/callback"

Response (200 OK — bare JSON, no envelope):

{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4...",
  "scope": "org",
  "scopes": "[\"org:12345:rw\",\"org:67890:rw\"]",
  "agent_name": "My MCP Agent"
}

Response Fields:

FieldTypeDescription
access_tokenstringJWT for API requests. Use as Authorization: Bearer {access_token}. Expires in 1 hour.
token_typestringAlways "Bearer"
expires_inintegerToken lifetime in seconds (3600 = 1 hour)
refresh_tokenstringLong-lived token for obtaining new access tokens. Store securely. Returned unchanged on refresh (no rotation).
scopestringThe legacy scope word that was granted (e.g. user, org)
scopesstringJSON-encoded array of scope strings in entity_type:entity_id:access_mode format. Emitted for every grant — a plain scope=user grant is stored explicitly as ["user:*:rw"] and reports it here. Persist this, not scope
agent_namestringAgent display name (v2.0 only, present when set during authorization)

scope versus scopes. scope (singular) is the legacy scope word; scopes (plural) is a JSON-encoded string listing the granted entity scopes. Persist scopes, not scope.

scopes is now returned for every grant, including a plain scope=user grant that previously carried no list — such a grant is stored explicitly as ["user:*:rw"]. Clients must not treat a missing scopes as “this is a full session”, and must not branch on auth_type === "jwt_v1": those grants now report jwt_v2 at introspection.

This response carries no admin and no legacy field — derive administrative capability from GET /current/auth/scopes/.

Refresh Token Exchange

ParameterTypeRequiredDescription
grant_typestringYesMust be "refresh_token"
refresh_tokenstringYesCurrent valid refresh token
client_idstringYesMust match the original token request
device_namestringNoUpdated device name for session tracking
device_typestringNoUpdated device type for session tracking
curl -X POST "https://api.fast.io/current/oauth/token/" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=refresh_token&refresh_token=dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4...&client_id=my-app"

Response (200 OK — bare JSON): Same format as authorization code exchange. Returns a new access_token. The refresh_token is long-lived and is returned unchanged (no per-refresh rotation).

Refresh narrows a demoted administrative grant. A grant naming a concrete entity at rwa whose human is no longer an administrator of it comes back as rw, and the response's scopes echoes the narrowed set — which is then what the session holds. Wildcard rwa grants are never narrowed this way; they are capped live on every request against the human's role. Losing access to an entity entirely still revokes, as before.

Error Responses (RFC 6749 SS5.2 format — bare JSON):

{
  "error": "invalid_grant",
  "error_description": "The authorization code is invalid or has expired."
}
ScenarioRFC 6749 ErrorHTTP Status
Missing/invalid parametersinvalid_request400
Invalid grant_type valueunsupported_grant_type400
code_verifier invalid length (not 43–128 chars)invalid_request400
Unrecognized resource indicatorinvalid_request400
Invalid/expired authorization codeinvalid_grant400
PKCE code_verifier mismatchinvalid_grant400
client_id mismatchinvalid_grant400
redirect_uri mismatchinvalid_grant400
Resource indicator mismatch (RFC 8707)invalid_grant400
Invalid/expired/revoked refresh tokeninvalid_grant400
client_id mismatch on refreshinvalid_grant400
Inactive user accountinvalid_grant400
Per-user active-session cap reached on authorization-code exchange (error_description begins "You have reached the maximum number of active connections") — revoke an existing session via DELETE /current/oauth/sessions/{session_id}/ and retryinvalid_grant400
Internal server failureserver_error500

Resource Indicator Enforcement: The resource value is stored in the authorization code during the authorize step. At token exchange, the value is strictly compared. If they do not match — including if one is null and the other is not — the exchange fails. This prevents downgrade attacks where a client omits the resource to obtain an unrestricted token.

Step 5: Use the Access Token

Include the access token in all API requests:

Authorization: Bearer {access_token}

When the access token expires (after 1 hour), use the refresh token to get a new one without requiring user interaction. Refresh proactively before expiration (5-minute buffer recommended) to avoid interruptions.

Token Revocation

POST /current/oauth/revoke/

Revoke a refresh token (logout). Implements RFC 7009 (OAuth 2.0 Token Revocation). Always returns success to prevent token enumeration attacks.

POST /current/oauth/revoke/

Auth: None · Content-Type: application/x-www-form-urlencoded

Request Parameters:

ParameterTypeRequiredDescription
tokenstringYesThe refresh token to revoke
curl -X POST "https://api.fast.io/current/oauth/revoke/" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "token=dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4..."

Response (200 OK):

{
  "result": true
}

Error Responses (RFC 6749 format — bare JSON):

ScenarioErrorHTTP Status
token parameter missinginvalid_request400

Per RFC 7009, this endpoint always returns success regardless of whether the token was found, was already revoked, or never existed. Always call this endpoint on user logout and clear local token storage regardless of the response.

Session Management

OAuth sessions represent active token grants. Each authorization code exchange creates a session. Sessions have a stable session_id (32-character hex string) that persists across refreshes.

GET /current/oauth/sessions/

List all active (non-expired, non-revoked) OAuth sessions for the authenticated user.

GET /current/oauth/sessions/

Auth: Required (Bearer JWT)

curl -X GET "https://api.fast.io/current/oauth/sessions/" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK):

{
  "result": true,
  "sessions": [
    {
      "session_id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
      "client_id": "my-app",
      "scopes": ["org:12345:rw"],
      "agent_name": "My MCP Agent",
      "device_name": "Chrome on macOS",
      "device_type": "desktop",
      "ip_address": "203.0.113.42",
      "last_used": "2026-01-22 14:00:00 UTC",
      "created": "2026-01-21 14:00:00 UTC",
      "expires": "2036-01-21 14:00:00 UTC",
      "admin": false,
      "legacy": false
    }
  ],
  "count": 1
}

Response Fields:

FieldTypeDescription
resultbooleantrue on success
response.sessionsarrayList of active session objects
response.sessions[].session_idstring32-character hex session identifier
response.sessions[].client_idstringOAuth client that created the session
response.sessions[].scopesarrayGranted scope strings in entity_type:entity_id:access_mode format. A scope=user grant is now stored explicitly as ["user:*:rw"]; an empty array means a legacy session that declares no scopes claim at all.
response.sessions[].agent_namestring or nullAgent display name if set during authorization, otherwise null
response.sessions[].device_namestringHuman-readable device description
response.sessions[].device_typestringDevice category: desktop, mobile, tablet, or unknown
response.sessions[].ip_addressstringIP address from the most recent refresh
response.sessions[].last_usedstringDatetime of last token refresh (YYYY-MM-DD HH:MM:SS UTC)
response.sessions[].createdstringDatetime when the session was created (YYYY-MM-DD HH:MM:SS UTC)
response.sessions[].expiresstringDatetime when the session expires (YYYY-MM-DD HH:MM:SS UTC)
response.sessions[].adminbooleanWhether the session carries at least one rwa scope, i.e. whether it can perform administrative operations (still capped by the human's live role)
response.sessions[].legacybooleanWhether the session declares no scopes claim at all. A legacy OAuth session behaves as user:*:rw: whole-account read and write, no administration, no account settings. legacy never implies admin, and admin never implies legacy
response.countintegerTotal number of active sessions

Error Responses:

ScenarioError CodeHTTP Status
Not authenticated1650 (Authentication Invalid)401
Internal error1654 (Internal Error)500

GET /current/oauth/sessions/{session_id}/

Get details of a specific OAuth session. The session must belong to the authenticated user.

GET /current/oauth/sessions/{session_id}/

Auth: Required (Bearer JWT)

Path Parameters:

ParameterTypeRequiredDescription
session_idstringYes32 hexadecimal characters
curl -X GET "https://api.fast.io/current/oauth/sessions/a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4/" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK):

{
  "result": true,
  "session": {
    "session_id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
    "client_id": "my-app",
    "scopes": ["org:12345:rw"],
    "agent_name": "My MCP Agent",
    "device_name": "Chrome on macOS",
    "device_type": "desktop",
    "ip_address": "203.0.113.42",
    "last_used": "2026-01-22 14:00:00 UTC",
    "created": "2026-01-21 14:00:00 UTC",
    "expires": "2036-01-21 14:00:00 UTC",
    "admin": false,
    "legacy": false
  }
}

Error Responses:

ScenarioError CodeHTTP Status
Not authenticated1650 (Authentication Invalid)401
session_id missing1605 (Invalid Input)406
session_id not 32 hex chars1605 (Invalid Input)406
Session not found or wrong user1609 (Not Found)404
Internal error1654 (Internal Error)500

PATCH /current/oauth/sessions/{session_id}/

Update the device_name, agent_name and/or scopes of a specific OAuth session. The session must belong to the authenticated user and must not be revoked.

PATCH /current/oauth/sessions/{session_id}/

Auth: Required (Bearer JWT)

Path Parameters:

ParameterTypeRequiredDescription
session_idstringYes32 hexadecimal characters

Body Parameters:

ParameterTypeRequiredDescription
device_namestringNoNew device name (max 128 characters; empty string clears to null)
agent_namestringNoNew agent name (max 128 characters; empty string clears to null)
scopesstringNoJSON-encoded list of scope strings, the same shape as an API key's. Must be narrower than or equal to both what the session already holds and what the calling credential holds

At least one of device_name, agent_name or scopes must be provided, so a scopes-only request is valid.

Narrowing only — there is no widening path. Submitted scopes are checked against the session's current scopes and against the credential making the request; a broader set is refused with 403 and 10768 (error.params.reason: scope_exceeds_issuer). An empty list ([]) is refused with 406 136396 — to end a session entirely use DELETE /current/oauth/sessions/{session_id}/. A narrowing takes effect for the access token at the next refresh.

curl -X PATCH "https://api.fast.io/current/oauth/sessions/a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4/" \
  -H "Authorization: Bearer {jwt_token}" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "device_name=My%20Work%20Laptop"

Response (200 OK):

{
  "result": true,
  "session": {
    "session_id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
    "client_id": "my-app",
    "scopes": ["org:12345:rw"],
    "agent_name": "My MCP Agent",
    "device_name": "My Work Laptop",
    "device_type": "desktop",
    "ip_address": "203.0.113.42",
    "last_used": "2026-01-22 14:00:00 UTC",
    "created": "2026-01-21 14:00:00 UTC",
    "expires": "2036-01-21 14:00:00 UTC",
    "admin": false,
    "legacy": false
  }
}

Error Responses:

ScenarioError CodeHTTP Status
Not authenticated1650 (Authentication Invalid)401
session_id missing1605 (Invalid Input)406
session_id not 32 hex chars1605 (Invalid Input)406
None of device_name, agent_name or scopes provided1605 (Invalid Input)406
scopes broader than the session or than the calling credential10768 (scope_exceeds_issuer)403
scopes submitted as an empty list ([])136396406
scopes no longer grantable to this user176886406
device_name exceeds 128 chars1605 (Invalid Input)406
agent_name exceeds 128 chars1605 (Invalid Input)406
Session is revoked1605 (Invalid Input)406
Session not found or wrong user1609 (Not Found)404
Internal error1654 (Internal Error)500

DELETE /current/oauth/sessions/{session_id}/

Revoke a specific OAuth session. The session must belong to the authenticated user. This operation is idempotent — if the session is already revoked, success is still returned.

DELETE /current/oauth/sessions/{session_id}/

Auth: Required (Bearer JWT)

Path Parameters:

ParameterTypeRequiredDescription
session_idstringYes32 hexadecimal characters
curl -X DELETE "https://api.fast.io/current/oauth/sessions/a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4/" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK):

{
  "result": true,
  "message": "Session has been revoked."
}

Error Responses:

ScenarioError CodeHTTP Status
Not authenticated1650 (Authentication Invalid)401
session_id missing1605 (Invalid Input)406
session_id not 32 hex chars1605 (Invalid Input)406
Session not found or wrong user1609 (Not Found)404
Internal error1654 (Internal Error)500

DELETE /current/oauth/sessions/

Revoke all OAuth sessions (logout everywhere). Optionally exclude the current session for "log out everywhere else" functionality.

DELETE /current/oauth/sessions/

Auth: Required (Bearer JWT)

Query Parameters:

ParameterTypeRequiredDefaultDescription
exclude_currentstringNoSet to "true" or "1" to keep the current session active
current_session_idstringNoSession ID to preserve when exclude_current is set. If omitted (or exclude_current is unset), all sessions are revoked.
# Revoke ALL sessions
curl -X DELETE "https://api.fast.io/current/oauth/sessions/" \
  -H "Authorization: Bearer {jwt_token}"

# Revoke all EXCEPT current session
curl -X DELETE "https://api.fast.io/current/oauth/sessions/?exclude_current=true&current_session_id=a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK):

{
  "result": true,
  "message": "All sessions have been revoked."
}

Or when exclude_current is used:

{
  "result": true,
  "message": "All other sessions have been revoked."
}

Error Responses:

ScenarioError CodeHTTP Status
Not authenticated1650 (Authentication Invalid)401
Internal error1654 (Internal Error)500

When exclude_current is "true" but current_session_id is not provided, all sessions are revoked.

Token Scope Introspection

GET /current/auth/scopes/

Returns the current token's scope information, auth type, and agent status. Enables clients to discover their token's capabilities without decoding the JWT.

GET /current/auth/scopes/

Auth: Required (Bearer JWT or API Key)

curl -X GET "https://api.fast.io/current/auth/scopes/" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK):

{
  "result": true,
  "auth_type": "jwt_v2",
  "scopes": ["org:12345:rw", "org:67890:rw"],
  "scopes_detail": [
    {
      "entity_type": "org",
      "entity_id": "12345",
      "access_mode": "rw",
      "admin": false,
      "name": "Acme Corp",
      "domain": "acme"
    },
    {
      "entity_type": "org",
      "entity_id": "67890",
      "access_mode": "rw",
      "admin": false,
      "name": "Beta Inc",
      "domain": "beta"
    }
  ],
  "is_agent": true,
  "agent_name": "My MCP Agent",
  "full_access": false,
  "admin": false,
  "legacy": false
}

Response (200 OK — API-key auth with scopes and expiration):

{
  "result": true,
  "auth_type": "api_key_scoped",
  "scopes": ["org:12345:rw"],
  "scopes_detail": [
    {
      "entity_type": "org",
      "entity_id": "12345",
      "access_mode": "rw",
      "admin": false,
      "name": "Acme Corp",
      "domain": "acme"
    }
  ],
  "is_agent": true,
  "agent_name": "CI Pipeline",
  "full_access": false,
  "admin": false,
  "legacy": false,
  "expires": "2026-12-31 23:59:59 UTC"
}

Response Fields:

FieldTypeDescription
resultbooleantrue on success
response.auth_typestring"jwt_v2" (scoped JWT), "jwt_v1" (legacy JWT), "api_key" (legacy API key that declares no scopes claim), or "api_key_scoped" (API key whose stored token record has a non-empty scopes claim)
response.scopesarrayScope strings in entity_type:entity_id:access_mode format. Empty only for a legacy credential that declares no scopes claim at all (a v1.0 JWT or a pre-scopes API key).
response.scopes_detailarrayHydrated scope objects with entity names and metadata, one per entry in scopes. Empty when scopes is empty.
response.is_agentbooleanWhether the token represents an agent
response.agent_namestring or nullAgent display name if set, otherwise null
response.full_accessbooleanWhether the credential is account-wide and may writeuser:*:rw or user:*:rwa. true for legacy (unscoped) API keys and browser login sessions; false for user:*:r and for entity-scoped credentials. It is not an administration flag — read admin for that
response.adminbooleanWhether the credential can perform administrative operations: true for a browser login session and for any credential holding an rwa scope (still capped by the human's live role). Always present
response.legacybooleanWhether the credential declares no scopes claim at all — a pre-scopes API key, a pre-scopes OAuth session, and every browser login session. Always present. legacy never implies admin, and admin never implies legacy
response.expiresstringAPI-key auth only: token expiration timestamp in canonical YYYY-MM-DD HH:MM:SS UTC format. Omitted entirely when the token has no expiration set or when auth is JWT-based.

Scope Detail Fields:

Each entry in scopes_detail contains entity-specific fields:

FieldTypePresent ForDescription
entity_typestringAlluser, org, workspace, share, fileshare, memory, or userdetails
entity_idstringAllNumeric ID or * for wildcard
access_modestringAllr (read), rw (read/write) or rwa (read/write/administer)
adminbooleanAlltrue when this entry's access mode is rwa, so a client need not re-parse the scope string
labelstringFull access / wildcardHuman-readable label (e.g., "Full Access", "All Organizations")
namestringOrg, WorkspaceEntity display name
domainstringOrgOrganization subdomain
folder_namestringWorkspaceWorkspace URL slug
org_idintegerWorkspace, ShareParent organization ID
org_namestringWorkspace, ShareParent organization name
org_domainstringWorkspace, ShareParent organization subdomain
titlestringShareShare display title
share_typestringSharesend, receive, or exchange
workspace_idintegerShareParent workspace ID
workspace_namestringShareParent workspace name
load_errorstringOn failure"Entity not found" if the referenced entity could not be loaded

Labels follow the access mode: user:*:rw renders as "Full Access", user:*:rwa as "Full Access (Admin)", user:*:r as "Entire account (Read Only)" and userdetails:*:rw as "Account settings"; other wildcards render in the same style, e.g. "All Organizations (Read/Write/Admin)".

How the flags combine:

Credentialfull_accessadminlegacyauth_type
user:*:rwatruetruefalsejwt_v2 / api_key_scoped
user:*:rwtruefalsefalsejwt_v2 / api_key_scoped
user:*:rfalsefalsefalsejwt_v2 / api_key_scoped
Scoped, e.g. org:123:rwafalsetruefalsejwt_v2 / api_key_scoped
Legacy key with no scopestruefalsetrueapi_key
Browser login sessiontruetruetruejwt_v1

Error Responses:

ScenarioError CodeHTTP Status
Not authenticated1650 (Authentication Invalid)401
Wrong HTTP method109463405

Complete PKCE Example Flow

Here is a complete example of the PKCE authorization flow:

0. CLIENT -> API: Discover server configuration (optional)
   GET /.well-known/oauth-authorization-server/
   -> Returns endpoints, grant types, PKCE methods, resource_indicators_supported

0b. CLIENT -> API: Register client dynamically (if no client_id)
   POST /current/oauth/register/
   Content-Type: application/json
   {"client_name": "My Agent", "redirect_uris": ["http://localhost:8080/callback"]}
   -> Returns client_id, registration_access_token

   OR use a CIMD URL as client_id (no registration needed)

1. CLIENT: Generate PKCE parameters
   code_verifier  = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
   code_challenge = base64url(sha256(code_verifier))
                  = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"

2. CLIENT -> BROWSER: Open authorization URL in user's browser
   GET /current/oauth/authorize/
   ?client_id=my-app
   &redirect_uri=http://localhost:8080/callback
   &response_type=code
   &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
   &code_challenge_method=S256
   &state=xyz123
   &resource=https://mcp.fast.io/mcp

3. API -> BROWSER: 302 redirect to login/consent page
   -> Browser lands on login page, user signs in, approves access

4. BROWSER -> CLIENT: Authorization code returned
   http://localhost:8080/callback?code=AUTH_CODE_HERE&state=xyz123
   (or displayed on screen for user to copy)

5. CLIENT -> API: Exchange code for tokens
   POST /current/oauth/token/
   Content-Type: application/x-www-form-urlencoded

   grant_type=authorization_code
   &code=AUTH_CODE_HERE
   &code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
   &client_id=my-app
   &redirect_uri=http://localhost:8080/callback
   &resource=https://mcp.fast.io/mcp

6. API -> CLIENT: Tokens returned (bare JSON)
   {
     "access_token": "eyJ...",
     "token_type": "Bearer",
     "expires_in": 3600,
     "refresh_token": "eyJ...",
     "scope": "user"
   }

7. CLIENT -> API: Use access token for requests
   GET /current/user/me/details/
   Authorization: Bearer eyJ...

8. CLIENT -> API: Refresh when access token expires
   POST /current/oauth/token/
   Content-Type: application/x-www-form-urlencoded

   grant_type=refresh_token
   &refresh_token=eyJ...
   &client_id=my-app

   -> Returns a new access_token; the same refresh_token is returned unchanged

9. CLIENT -> API: Revoke on logout
   POST /current/oauth/revoke/
   Content-Type: application/x-www-form-urlencoded

   token=eyJ...

Error Handling

Token and Revoke Endpoints (RFC 6749 SS5.2)

The token (POST /current/oauth/token/) and revoke (POST /current/oauth/revoke/) endpoints return RFC 6749 compliant error responses — bare JSON, not the standard platform envelope:

{
  "error": "invalid_grant",
  "error_description": "The authorization code is invalid or has expired."
}

Registration Endpoint (RFC 7591)

The registration endpoint (POST /current/oauth/register/ and PUT /current/oauth/register/) also returns bare JSON error responses with RFC 7591 error codes:

{
  "error": "invalid_client_metadata",
  "error_description": "The client_name must be between 1 and 128 characters."
}

Other OAuth Endpoints

All other OAuth endpoints (authorize, authorize/info, sessions) use the standard platform error envelope:

{
  "result": false,
  "error": {
    "code": 1605,
    "text": "Error message",
    "documentation_url": "https://api.fast.io/llms.txt",
    "resource": "GET /current/oauth/authorize/"
  }
}
ScenarioError CodeHTTP Status
Rate limited1671 (Rate Limited)429
↑ Back to top