OAuth2
OAuth2 is how a user grants an application access to their account. The same consent step can install the application’s bot into a guild or group direct message. Application records, client secrets, and bot credentials belong to the Applications resource.
Authorisation code grant
Section titled “Authorisation code grant”The grant begins at Authorise application. That route redirects the browser to the Fluxer consent interface, unless the caller asked for a silent authorisation. Grant OAuth2 consent records the decision, issues one authorisation code, and returns the callback URL that has it. The client presents that code to Exchange OAuth2 token with its client credentials and receives an access and refresh token pair.
Proof Key for Code Exchange, or PKCE, supports the S256 and plain challenge methods, and Fluxer binds the challenge to the code when it issues the code. A code_challenge supplied without a code_challenge_method is treated as plain. An exchange for a code that has a challenge supplies a matching code_verifier, and an exchange for a code that has none ignores any verifier it is given.
Credentials and lifetimes
Section titled “Credentials and lifetimes”Fluxer generates an authorisation code, a client secret, an access token, and a refresh token as 32 random bytes encoded as base64url, and a bot token is the application ID, a full stop, then such a secret. Retrying an interrupted exchange with the same inputs is rejected as an invalid grant, and the client restarts the grant.
An authorisation code expires 10 minutes after it is issued, an access token after 7 days, and a refresh token after 30 days. Every refresh exchange issues a replacement with a new 30 day life, so a grant ends 30 days after its last refresh.
Route authentication
Section titled “Route authentication”Every route with a delegated identity is user-only unless the operation says otherwise. Fluxer rejects a bot token and an OAuth2 bearer with 403 ACCESS_DENIED, and accepts an account with an outstanding required action. The token, introspection, and revocation routes authenticate the client application instead, so their rate limit is keyed by client IP address.
OAuth2 string normalisation
Section titled “OAuth2 string normalisation”Fluxer trims every bounded OAuth2 string field before it checks its length. Each such field accepts 1 to 256 UTF-16 code units, redirect_uri included.
Fluxer validates, stores, compares, forwards, and returns the normalised value. The permissions field and the Grant OAuth2 consent response_type field are read verbatim and have no length bound of their own. HTTP Basic client credentials do not pass through this request field normalisation. The PKCE code_challenge and code_verifier are normalised too, so a client MUST NOT rely on surrounding whitespace surviving.
OAuth2 scopes
Section titled “OAuth2 scopes”A scope names one thing the application may read or do on the user’s behalf.
| Value | Description |
|---|---|
| identify1 | Read the authorised user’s account identity |
| email2 | Include the authorised user’s email address and email verification state |
| guilds3 | Read the guilds the authorised user is a member of |
| connections4 | Read the authorised user’s connections |
| bot5 | Install the application’s bot account into a selected guild or group direct message |
1 Grants Get OAuth2 user information and the reduced bearer representation from Get current user, and is also required before Get current OAuth2 authorisation includes a user object
2 Has no observable effect unless identify is granted as well, because every route that exposes the address also requires identify
3 Grants List current user guilds, Get guild, and List guild roles
4 Grants List connections
5 Installs a bot account and grants no bearer access of its own, so an application whose grants are bot alone is never presented as an authorisation by List OAuth2 authorisations
An unrecognised scope value is rejected when the authorisation request is completed. Fluxer splits a scope string on runs of whitespace and + characters, and discards empty segments. A repeated scope value is granted once.
The scope string of an OAuth2 token and of an OAuth2 introspection is sorted into the registry order above. The scopes array of a current OAuth2 authorisation and of an OAuth2 authorisation has the stored scopes in no defined order, with unrecognised values removed.
OAuth2 error response object
Section titled “OAuth2 error response object”The failure envelope the OAuth2 routes return.
Structure
Section titled “Structure”| Field | Type | Description |
|---|---|---|
| error | string | The OAuth2 error code naming the failure |
| error_description1 | string | The human-readable description of the failure |
1 Always present, and some descriptions are a bare API error code identifier
These two members are the whole body, so the routine that reads the ordinary error response cannot parse it. Fluxer returns every OAuth2 failure with status 400. A client MUST key on error and never on error_description.
The consent, token, introspection, and revocation routes return this object for a grant, client authentication, scope, redirect, response type, or permission mask failure. Every other failure, including request validation, authentication, authorisation, and rate limiting, uses the ordinary error response with an API error code.
Example
Section titled “Example”{ "error": "invalid_grant", "error_description": "Authorization code is invalid or expired"}OAuth2 error codes
Section titled “OAuth2 error codes”| Value | Description |
|---|---|
| invalid_request2 | The request itself is malformed |
| invalid_client3 | Client authentication failed |
| invalid_grant4 | The presented grant is not usable |
| invalid_scope | The requested scope contains a value that is not in the scope registry |
2 The redirect URI is missing, unregistered, or unparseable, the response type is not code on a request that asks for a non-bot scope, the requested permission mask is not a non-negative integer literal, or client credentials are supplied in both an Authorization header and the form
3 The client application is unknown, supplied no client secret, or supplied a client secret that does not match the stored hash
4 The authorisation code or refresh token is unknown, expired, already used, bound to another application, or bound to another redirect URI, or the authorisation code has a PKCE challenge the presented verifier does not prove
OAuth2 token object
Section titled “OAuth2 token object”The access and refresh token pair a successful token exchange returns.
Structure
Section titled “Structure”| Field | Type | Description |
|---|---|---|
| access_token | string | The bearer token the application presents in the Authorization header |
| token_type | string | The token type, always Bearer |
| expires_in1 | integer | The access token lifetime in seconds, always 604800 |
| refresh_token2 | string | The refresh token that replaces this pair when it is exchanged |
| scope | string | The granted scopes, space separated in registry order |
1 The configured access token lifetime, restated in every successful response
2 Every grant Fluxer issues is bound to a user, so a successful exchange always returns a refresh token
Example
Section titled “Example”{ "access_token": "M2xQb1RkY0hqTmZLc1B2WndBcUx1RWc2WXRJbk9iUmE", "token_type": "Bearer", "expires_in": 604800, "refresh_token": "VGhpc0lzQUZha2VSZWZyZXNoVG9rZW5Gb3JEb2NzMDE", "scope": "identify email"}OAuth2 user information object
Section titled “OAuth2 user information object”The account identity an application reads with the identify scope.
Structure
Section titled “Structure”| Field | Type | Description |
|---|---|---|
| sub | snowflake | The ID of the subject user, equal to id |
| id | snowflake | The ID of the account |
| username | string | The username of the account |
| discriminator | string | The four-digit discriminator tag |
| global_name | ?string | The display name of the account, or null when unset |
| avatar1 | ?string | The avatar hash of the account, or null when it has none |
| email?2 | ?string | The email address of the account, null unless the email scope is granted and an address is set |
| verified?3 | ?boolean | Whether the address has been verified |
| flags?4 | integer | The public user flags on the account |
| avatar_color5 | ?integer | The default avatar colour |
| bot5 | boolean | Whether the account is a bot |
| system5 | boolean | Whether the account is a system user |
1 An animated avatar hash has the a_ prefix, and the prefix is removed while the account has no animated avatar entitlement
2 Always emitted
3 Emitted only when the email scope is granted and the account has an address. An unverified address reports false
4 Declared optional by the response schema and populated on every response
5 Emitted on every response although the response schema does not declare it, so a client MUST NOT depend on it
OAuth2 introspection object
Section titled “OAuth2 introspection object”The liveness and scope report Fluxer returns for one presented token.
Structure
Section titled “Structure”| Field | Type | Description |
|---|---|---|
| active | boolean | Whether the presented token is live and belongs to the authenticated client |
| scope? | string | The granted scopes, space separated in registry order |
| client_id? | snowflake | The ID of the application the token was issued to |
| username?1 | string | A reserved member that Fluxer never populates |
| token_type? | string | The kind of token presented, Bearer for an access token and refresh_token for a refresh token |
| exp?2 | integer | The access token expiry time in Unix seconds |
| iat? | integer | The issue time in Unix seconds |
| sub?3 | snowflake | The ID of the subject user |
1 Absent from every response
2 Absent for a refresh token, which still expires 30 days after it is issued
3 Every token Fluxer issues is bound to a user, so an active result always has a subject
Apart from username and exp, every optional field above is present when active is true and absent when it is false. An active access token has exp and an active refresh token does not.
Fluxer reports an unknown token, an expired token, and a token issued to another application as inactive. Introspection never resolves the account behind the token, so an active result reports on the token alone.
Current OAuth2 authorisation object
Section titled “Current OAuth2 authorisation object”The application, scopes, and expiry behind the access token the caller presented.
Structure
Section titled “Structure”| Field | Type | Description |
|---|---|---|
| application | OAuth2 application summary object | The application the access token was issued to |
| scopes1 | array[string] | The granted scopes, with unrecognised values removed |
| expires | ISO8601 timestamp | The expiry time of the access token |
| user?2 | OAuth2 authorised user object | The account the access token was issued for |
1 Emitted in no defined order, and bot is retained here even though it grants no delegated read
2 Present only when the token grants identify and the account still exists, so a token restricted to bot never has one
OAuth2 application summary object
Section titled “OAuth2 application summary object”The reduced application record in a current OAuth2 authorisation.
Structure
Section titled “Structure”| Field | Type | Description |
|---|---|---|
| id | snowflake | The ID of the application |
| name | string | The name of the application |
| icon1 | ?string | The icon hash, always null on this object |
| description1 | ?string | The description, always null on this object |
| bot_public | boolean | Whether any eligible user can install the bot |
| bot_require_code_grant | boolean | Whether bot installation requires an OAuth2 code grant |
| flags2 | integer | The application flags, always 0 |
1 Null even when the application’s bot has an avatar or biography
2 No application flag bit is defined
OAuth2 authorised user object
Section titled “OAuth2 authorised user object”The account a current OAuth2 authorisation was issued for.
Structure
Section titled “Structure”| Field | Type | Description |
|---|---|---|
| id | snowflake | The ID of the account |
| username | string | The username of the account |
| discriminator | string | The four-digit discriminator tag |
| global_name | ?string | The display name of the account, or null when unset |
| avatar1 | ?string | The avatar hash of the account, or null when it has none |
| avatar_color | ?integer | The default avatar colour |
| bot?2 | boolean | Whether the account is a bot |
| system?2 | boolean | Whether the account is a system user |
| flags | integer | The public user flags on the account |
| email?3 | ?string | The email address of the account, null unless the email scope is granted and an address is set |
| verified?4 | ?boolean | Whether the address has been verified |
| sub5 | snowflake | The ID of the subject user, equal to id |
1 An animated avatar hash has the a_ prefix, and the prefix is removed while the account has no animated avatar entitlement
2 Emitted on every response, and false when the account is neither
3 Always emitted
4 Emitted only when the email scope is granted and the account has an address
5 Emitted on every response although the response schema does not declare it, so a client MUST NOT depend on it
OAuth2 authorisation object
Section titled “OAuth2 authorisation object”One application the current user has authorised, aggregated across its live refresh tokens.
Structure
Section titled “Structure”| Field | Type | Description |
|---|---|---|
| application | authorised application object | The application the user has granted access to |
| scopes1 | array[string] | The scopes aggregated across the user’s live refresh tokens for the application |
| authorized_at2 | ISO8601 timestamp | The earliest creation time among the refresh tokens that contributed to this entry |
1 The refresh token that first contributes an application contributes only its recognised scopes other than bot. Every further refresh token for the same application contributes its stored scope set unfiltered, so bot and an unrecognised value can appear once more than one refresh token contributes
2 Lowered by every refresh token that contributes to the entry, including one that has only bot
An authorisation appears only while the application holds a live refresh grant with a recognised scope other than bot.
Authorised application object
Section titled “Authorised application object”The application named by an OAuth2 authorisation entry.
Structure
Section titled “Structure”| Field | Type | Description |
|---|---|---|
| id | snowflake | The ID of the application |
| name | string | The name of the application |
| icon1 | ?string | The avatar hash of the application’s bot account |
| description2 | ?string | The description, always null on this object |
| bot_public | boolean | Whether any eligible user can install the bot |
1 Null when the application has no bot account or that account has no avatar. The stored hash is reported unfiltered, so an animated hash retains its a_ prefix
2 An application has no independent description
OAuth2 consent response object
Section titled “OAuth2 consent response object”The callback URL Grant OAuth2 consent returns after it records the decision.
Structure
Section titled “Structure”| Field | Type | Description |
|---|---|---|
| redirect_to1 | string | The absolute URL with the authorisation result |
1 The URL has the code query parameter, and the normalised caller state when one was supplied
Fluxer builds the URL from the registered redirect URI the request resolved to, which is the Fluxer application endpoint when a bot-only authorisation supplied none. The code records the URL serialisation of that resolved URI, and the client later presents that exact serialisation to Exchange OAuth2 token.
Authorise application
Section titled “Authorise application”GET/v1/oauth2/authorizeStarts an authorisation request. A query that passes request validation always answers with a redirect, and authentication is optional.
Query parameters
Section titled “Query parameters”| Field | Type | Description |
|---|---|---|
| response_type? | string | The OAuth2 response type, code when supplied |
| client_id | snowflake | The ID of the application being authorised |
| redirect_uri?1 | string | The absolute redirect URI registered by the application (1-256 UTF-16 code units after normalisation) |
| scope | string | The requested OAuth2 scopes, space separated (1-256 UTF-16 code units after normalisation) |
| state?4 | string | The caller state (1-256 UTF-16 code units after normalisation) |
| prompt? | string | The prompt mode, either consent or none |
| guild_id?2 | snowflake | The guild pre-selected for the bot scope |
| channel_id?2 | snowflake | The group direct message pre-selected for the bot scope |
| permissions?3 | string | The bot permission integer literal |
| disable_guild_select? | string | Whether the consent interface suppresses guild selection, as the literal true or false |
| code_challenge? | string | The PKCE challenge bound to the issued code (1-256 UTF-16 code units after normalisation) |
| code_challenge_method? | string | The PKCE method, either S256 or plain, defaulting to plain when a challenge is supplied without it |
1 The value must parse as an absolute URL with a scheme and a host, and must match a registered redirect URI before a code is issued
2 Both values are forwarded to the consent interface unchanged, and their mutual exclusion is enforced by Grant OAuth2 consent
3 The value is forwarded to the consent interface unchanged and is parsed as an integer only by Grant OAuth2 consent
4 The value is returned unchanged after normalisation, on both the success redirect and the error redirect
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 302 | empty | The consent interface, a successful client callback, or an OAuth2 error callback was selected |
The 302 Location header is the selected redirect.
Without prompt=none, the redirect targets the Fluxer consent interface and forwards every supplied parameter, so no application, scope, or redirect validation happens yet.
With prompt=none, a request that resolves no user redirects with error=login_required. Every other silent failure redirects with error=consent_required. That covers every failure to issue a code, including an unknown application, an unrecognised scope, a missing or unregistered redirect URI, and a private bot the caller does not own. It also covers a requested non-bot scope the user has not already granted to this application. A silent request that asks only for bot skips the prior-grant test.
An error redirect has error and the normalised state when the caller supplied one. It has no error_description, and never the more specific OAuth2 error code the underlying failure raised.
Fluxer compares the two strings byte for byte, so a differing scheme, port, trailing slash, query, or fragment does not match. Every other case redirects to the Fluxer application endpoint. That covers an omitted redirect URI, an unknown application, and an application with no registered URI at all.
Side effects
Section titled “Side effects”A successful prompt=none request issues one authorisation code bound to the application, user, resolved redirect URI, granted scopes, and PKCE challenge. The code expires after 10 minutes. Nothing else changes. Only Grant OAuth2 consent installs a bot.
Rate limit
Section titled “Rate limit”60 requests per minute for each authenticated user, or for each client IP address when the request has no credential, on the oauth:authorize bucket.
Grant OAuth2 consent
Section titled “Grant OAuth2 consent”POST/v1/oauth2/authorize/consentCompletes an authorisation request. Returns an OAuth2 consent response object with the callback URL.
Installing a bot into a guild requires MANAGE_GUILD or ADMINISTRATOR in that guild. Installing it into a group direct message requires the caller to be a recipient of that channel.
A guild installation emits Guild Create, Guild Member Add, and, unless the guild suppresses join notifications, Message Create. When a non-zero permission mask is requested it also emits Guild Role Create and Guild Member Update. A group direct message installation emits Channel Create to the bot, Channel Recipient Add to the existing recipients, and Message Create.
JSON body
Section titled “JSON body”| Field | Type | Description |
|---|---|---|
| response_type?1 | string | The OAuth2 response type |
| client_id | snowflake | The ID of the application being authorised |
| redirect_uri?2 | string | The absolute redirect URI registered by the application (1-256 UTF-16 code units after normalisation) |
| scope | string | The requested OAuth2 scopes, space separated (1-256 UTF-16 code units after normalisation) |
| state? | string | The caller state appended to the callback URL (1-256 UTF-16 code units after normalisation) |
| permissions?3 | string | The bot permission integer literal requested for the installed bot |
| guild_id?4 | snowflake | The guild the bot is installed into |
| channel_id?4 | snowflake | The group direct message the bot is installed into |
| code_challenge? | string | The PKCE challenge bound to the issued code (1-256 UTF-16 code units after normalisation) |
| code_challenge_method? | string | The PKCE method, either S256 or plain, defaulting to plain when a challenge is supplied without it |
1 On a request that asks for any scope other than bot, an omitted value defaults to code and a supplied value must be code. On a request that asks for the bot scope alone the field is ignored
2 Required unless the request asks for the bot scope alone, and it must match a registered redirect URI. A bot-only request still requires it when the application sets bot_require_code_grant
3 The value must parse as a non-negative integer literal. Every bit outside the defined permission mask is cleared before use, and a caller without ADMINISTRATOR cannot request a retained bit it does not itself hold
4 The two fields are mutually exclusive
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | OAuth2 consent response object | Consent completed |
| 4005 | error response | The body fails validation, or the bot installation target is not usable |
| 4006 | OAuth2 error object | The application, scope, redirect URI, response type, or permission mask is rejected |
| 4037 | error response | The credential or the caller’s permissions do not allow the installation |
| 404 | error response | The selected channel does not exist (UNKNOWN_CHANNEL), or the selected guild does not exist (UNKNOWN_GUILD) |
5 Both installation targets are supplied, the selected channel is not a group direct message (INVALID_CHANNEL_TYPE), the application has no bot (NOT_A_BOT_APPLICATION), the bot is already a member of the selected guild (BOT_ALREADY_IN_GUILD), the guild has reached its member limit (MAX_GUILD_MEMBERS) or its role limit (MAX_GUILD_ROLES), or the group direct message has reached its recipient limit (MAX_GROUP_DM_RECIPIENTS)
6 The application is unknown, a scope is unrecognised, the redirect URI is missing or unregistered, the response type is not code on a request that asks for a non-bot scope, or the permission mask is not a non-negative integer literal
7 The credential is a bot token or an OAuth2 bearer (ACCESS_DENIED), the bot is private and the caller does not own the application (BOT_IS_PRIVATE), the caller holds neither MANAGE_GUILD nor ADMINISTRATOR in the selected guild, or requests a permission bit it does not itself hold (MISSING_PERMISSIONS), or the caller is not a recipient of the selected channel (MISSING_ACCESS)
Side effects
Section titled “Side effects”Consent issues one authorisation code bound to the application, user, resolved redirect URI, granted scopes, and PKCE challenge, and returns the callback URL that has it.
With the bot scope and a guild target, consent adds the bot to the guild with the bot-invite join source and the caller recorded as inviter, bypassing both the bot account’s own guild count limit and the guild ban list. The guild member limit still applies. When a non-zero permission mask is requested, it also creates one role with exactly those permissions, names the role after the application, and assigns it to the bot.
That role is created at position 1 with no colour and is neither hoisted nor mentionable. It consumes a guild role slot and is not removed when the bot leaves. The installation records bot add, role create, and member role update audit log entries.
With the bot scope and a group direct message target, it adds the bot as a recipient and stores the recipient-add system message.
When any part of the installation fails, Fluxer cancels the newly issued authorisation code, so the caller never receives a usable code.
Rate limit
Section titled “Rate limit”60 requests per minute for each authenticated user, on the oauth:authorize bucket.
Exchange OAuth2 token
Section titled “Exchange OAuth2 token”POST/v1/oauth2/tokenExchanges an authorisation code or a refresh token. Returns an OAuth2 token object on success.
The request body is application/x-www-form-urlencoded or multipart/form-data. The client application authenticates with HTTP Basic or with the client_id and client_secret form fields. No user credential is accepted.
Earlier access tokens for the same user and application survive a refresh and expire on their own schedule.
Request headers
Section titled “Request headers”| Field | Type | Description |
|---|---|---|
| Authorization?1 | string | The HTTP Basic client credentials |
| Content-Type | string | The request media type, either application/x-www-form-urlencoded or multipart/form-data |
1 This route resolves the client from the form first and falls back to the header, so supplying both is accepted. The introspection and revocation routes reject that combination as invalid_request
Form body
Section titled “Form body”Authorisation code fields
Section titled “Authorisation code fields”| Field | Type | Description |
|---|---|---|
| grant_type | string | The grant type, always authorization_code on this variant |
| code | string | The authorisation code returned by the callback (1-256 UTF-16 code units after normalisation) |
| redirect_uri1 | string | The redirect URI the authorisation request resolved to (1-256 UTF-16 code units after normalisation) |
| client_id?2 | snowflake | The client ID, which takes precedence over the HTTP Basic value |
| client_secret?2 | string | The client secret, which takes precedence over the HTTP Basic value (1-256 UTF-16 code units after normalisation) |
| code_verifier?3 | string | The PKCE verifier proving the challenge stored on the code (1-256 UTF-16 code units after normalisation) |
1 Compared as an exact string against the URI recorded on the code, which the OAuth2 consent response describes. A trailing slash, host casing, or percent-encoding difference is rejected as an invalid grant
2 Each credential field is resolved on its own, so a form value is used when it is supplied and the HTTP Basic value is used otherwise, and a form client_id can be combined with an HTTP Basic client secret. A request that resolves no client ID or no client secret is rejected as invalid_client
3 Required when the authorisation code has a PKCE challenge. For the S256 method it is hashed with SHA-256 and base64url-encoded before comparison with the challenge stored on the code, and for plain it is compared unchanged
When the code is PKCE-bound, an omitted or incorrect verifier is rejected as invalid_grant in the OAuth2 error response.
Refresh token fields
Section titled “Refresh token fields”| Field | Type | Description |
|---|---|---|
| grant_type | string | The grant type, always refresh_token on this variant |
| refresh_token | string | The refresh token to exchange (1-256 UTF-16 code units after normalisation) |
| client_id?1 | snowflake | The client ID, which takes precedence over the HTTP Basic value |
| client_secret?1 | string | The client secret, which takes precedence over the HTTP Basic value (1-256 UTF-16 code units after normalisation) |
1 Each credential field is resolved on its own, so a form value is used when it is supplied and the HTTP Basic value is used otherwise, and a form client_id can be combined with an HTTP Basic client secret. A request that resolves no client ID or no client secret is rejected as invalid_client
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | OAuth2 token object | A token pair was issued |
| 400 | error response | The form fails validation, including an unsupported grant_type |
| 4002 | OAuth2 error object | Client authentication or the presented grant fails |
2 Client authentication, the authorisation code, the refresh token, the redirect URI, or a required PKCE verifier is missing or does not prove the grant
An authorisation code is single-use. Fluxer consumes it before it issues the token pair. A replayed code is rejected as an invalid grant. Deleting an account purges every OAuth2 access and refresh token it holds, so a refresh exchange for a deleted account is rejected as an invalid grant as well. An authorisation code issued before the deletion still exchanges until it expires.
Fluxer verifies client authentication before it examines the grant, so an unknown application, an absent client secret, and a wrong client secret are all reported as invalid_client.
Any other grant_type, client_credentials included, fails request validation with the ordinary error response.
Side effects
Section titled “Side effects”A successful exchange issues one access token that expires after 7 days and one refresh token that expires after 30 days. An authorisation code exchange consumes the code, and a refresh exchange consumes the presented refresh token. Nothing else changes and no Gateway Dispatch is emitted.
Rate limit
Section titled “Rate limit”120 requests per minute for each client IP address, on the oauth:token bucket.
Get OAuth2 user information
Section titled “Get OAuth2 user information”GET/v1/oauth2/userinfoReturns the OAuth2 user information object for the account the token was issued to. Requires the identify scope.
An OAuth2 bearer access token is required. The email scope populates the address fields.
Request headers
Section titled “Request headers”| Field | Type | Description |
|---|---|---|
| Authorization | string | The OAuth2 bearer access token |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | OAuth2 user information object | User information was returned |
| 4011 | error response | The bearer token is missing, unusable, or expired |
| 403 | error response | The token lacks identify (MISSING_OAUTH_SCOPE) |
1 The credential is missing, is not an OAuth2 bearer access token, or has expired (UNAUTHORIZED), or the bearer no longer resolves to a user and an application (INVALID_TOKEN)
A missing scope failure has the required scope as a top-level required_scope member of the error response, so a client can request the correct authorisation without parsing the message text.
Rate limit
Section titled “Rate limit”120 requests per minute for each pair of authenticated user and bearer application, on the oauth:introspect bucket.
Introspect OAuth2 token
Section titled “Introspect OAuth2 token”POST/v1/oauth2/introspectReturns an OAuth2 introspection object describing one access or refresh token.
The request body is application/x-www-form-urlencoded or multipart/form-data. The client application authenticates with HTTP Basic or with the client_id and client_secret form fields. No user credential is accepted.
Request headers
Section titled “Request headers”| Field | Type | Description |
|---|---|---|
| Authorization? | string | The HTTP Basic client credentials |
| Content-Type | string | The request media type, either application/x-www-form-urlencoded or multipart/form-data |
Form body
Section titled “Form body”| Field | Type | Description |
|---|---|---|
| token | string | The access or refresh token to inspect (1-256 UTF-16 code units after normalisation) |
| client_id?1 | snowflake | The client ID, supplied when HTTP Basic is not used |
| client_secret?1 | string | The client secret, supplied when HTTP Basic is not used (1-256 UTF-16 code units after normalisation) |
1 Supplying either field together with a well-formed HTTP Basic Authorization header is rejected as invalid_request
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | OAuth2 introspection object | Introspection completed, including a token reported as inactive |
| 400 | error response | The form fails validation |
| 4002 | OAuth2 error object | Client authentication failed |
2 Client credentials are supplied in both the header and the form (invalid_request), no client secret is supplied, the client is unknown, or the client secret is wrong (invalid_client)
Fluxer ignores an Authorization header that is not well-formed HTTP Basic and reads the client credentials from the form.
Only a token issued to the authenticated client is reported as active.
Rate limit
Section titled “Rate limit”120 requests per minute for each client IP address, on the oauth:introspect bucket.
Revoke OAuth2 token
Section titled “Revoke OAuth2 token”POST/v1/oauth2/token/revokeRevokes a presented access or refresh token.
The request body is application/x-www-form-urlencoded or multipart/form-data. The client application authenticates with HTTP Basic or with the client_id and client_secret form fields. No user credential is accepted.
Request headers
Section titled “Request headers”| Field | Type | Description |
|---|---|---|
| Authorization? | string | The HTTP Basic client credentials |
| Content-Type | string | The request media type, either application/x-www-form-urlencoded or multipart/form-data |
Form body
Section titled “Form body”| Field | Type | Description |
|---|---|---|
| token | string | The access or refresh token to revoke (1-256 UTF-16 code units after normalisation) |
| token_type_hint?1 | string | The kind of token presented, either access_token or refresh_token |
| client_id?2 | snowflake | The client ID, supplied when HTTP Basic is not used |
| client_secret?2 | string | The client secret, supplied when HTTP Basic is not used (1-256 UTF-16 code units after normalisation) |
1 Only refresh_token matches the presented value against refresh tokens, and access tokens are still tried when no refresh token matches. An absent hint and an access_token hint match access tokens only
2 Supplying either field together with a well-formed HTTP Basic Authorization header is rejected as invalid_request
A client that presents a refresh token MUST send token_type_hint=refresh_token, or the revocation takes no effect.
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | empty | The revocation request was processed, including a token that matched nothing |
| 400 | error response | The form fails validation |
| 4003 | OAuth2 error object | Client authentication failed |
3 Client credentials are supplied in both the header and the form (invalid_request), no client secret is supplied, the client is unknown, or the client secret is wrong (invalid_client)
Fluxer ignores an Authorization header that is not well-formed HTTP Basic and reads the client credentials from the form.
An unknown token and a token issued to another application both return 200, so the response never reveals whether a token exists.
Side effects
Section titled “Side effects”On a match, Fluxer deletes the user’s complete token set for the authenticated application. Those tokens stop authenticating requests at once. A token that matched nothing mutates no state.
No Gateway Dispatch is emitted, so a client holding a revoked token learns of the revocation from its next rejected request.
Rate limit
Section titled “Rate limit”120 requests per minute for each client IP address, on the oauth:introspect bucket.
Get current OAuth2 authorisation
Section titled “Get current OAuth2 authorisation”GET/v1/oauth2/@meReturns the current OAuth2 authorisation object for the presented access token. Requires no scope.
An OAuth2 bearer access token is required, and the token must belong to a non-bot account. The response has a user object only when the token holds identify.
Request headers
Section titled “Request headers”| Field | Type | Description |
|---|---|---|
| Authorization | string | The OAuth2 bearer access token |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | current OAuth2 authorisation object | The authorisation was returned |
| 4011 | error response | The bearer token is missing, unusable, or expired |
| 403 | error response | The token belongs to a bot account (ACCESS_DENIED) |
1 The credential is missing, is not an OAuth2 bearer access token, or has expired (UNAUTHORIZED), the bearer has no user identity (UNAUTHORIZED), or it names an application that no longer exists (INVALID_TOKEN)
Rate limit
Section titled “Rate limit”120 requests per minute for each pair of authenticated user and bearer application, on the oauth:introspect bucket.
List OAuth2 authorisations
Section titled “List OAuth2 authorisations”GET/v1/oauth2/@me/authorizationsReturns the OAuth2 authorisation objects the current user has granted to applications.
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | array[OAuth2 authorisation object] | The authorisations were returned |
| 403 | error response | The credential is a bot token or an OAuth2 bearer (ACCESS_DENIED) |
An application appears in the listing only while it holds a live refresh grant with a recognised scope other than bot. A deleted application is omitted. The listing is unpaginated.
Rate limit
Section titled “Rate limit”60 requests per minute for each authenticated user, on the oauth_dev:clients:list bucket.
Revoke OAuth2 authorisation
Section titled “Revoke OAuth2 authorisation”DELETE/v1/oauth2/@me/authorizations/{applicationId}Revokes the current user’s authorisation for one application.
Path parameters
Section titled “Path parameters”| Field | Type | Description |
|---|---|---|
| applicationId | snowflake | The ID of the application whose authorisation is revoked |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 204 | empty | The authorisation was revoked, including when no grant remained |
| 400 | error response | The application ID is not a valid snowflake |
| 403 | error response | The credential is a bot token or an OAuth2 bearer (ACCESS_DENIED) |
| 404 | error response | The application does not exist (UNKNOWN_APPLICATION) |
Fluxer resolves the application before it deletes any token, so a 404 leaves the grant untouched.
Side effects
Section titled “Side effects”Revocation deletes every access token and refresh token the current user holds for the named application. Those tokens stop authenticating requests at once.
A bot the application installed stays in every guild and group direct message it joined. No Gateway Dispatch is emitted.
Rate limit
Section titled “Rate limit”120 requests per minute for each authenticated user, on the oauth:introspect bucket.
Bulk revoke OAuth2 authorisations
Section titled “Bulk revoke OAuth2 authorisations”POST/v1/oauth2/@me/authorizations/revokeRevokes the current user’s authorisations for several applications.
JSON body
Section titled “JSON body”| Field | Type | Description |
|---|---|---|
| application_ids1 | array[snowflake] | The IDs of the applications to revoke (1-100) |
1 Repeated IDs are collapsed before any revocation is performed
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 204 | empty | The authorisations were revoked |
| 400 | error response | The body fails validation, including an empty or oversized ID array |
| 403 | error response | The credential is a bot token or an OAuth2 bearer (ACCESS_DENIED) |
| 404 | error response | One of the named applications does not exist (UNKNOWN_APPLICATION) |
Fluxer resolves every named application before it deletes any token. One unknown application leaves every named application untouched.
Side effects
Section titled “Side effects”Revocation deletes every access token and refresh token the current user holds for each named application. Those tokens stop authenticating requests at once. Bot memberships are unaffected and no Gateway Dispatch is emitted.
Rate limit
Section titled “Rate limit”120 requests per minute for each authenticated user, on the oauth:introspect bucket.