OAuth2

OAuth2 enables application developers to build applications that utilize authentication and data from the Discord API. Within Discord, there are multiple types of OAuth2 authentication. Supported grants include the authorization code grant, implicit grant, device grant, client credentials, and some modified special-for-Discord flows for bots and webhooks.

Shared Resources

The first step in implementing OAuth2 is registering a developer application and retrieving your client ID and client secret. Most people who will be implementing OAuth2 will want to find and utilize a library in the language of their choice. For those implementing OAuth2 from scratch, please see RFC 6749 for details. After you create your application with Discord, make sure that you have your client_id and client_secret handy. The next step is to figure out which OAuth2 flow is right for your purposes.

OAuth2 URLs
OAuth2 Scopes

These are all the OAuth2 scopes that Discord supports. Some scopes require approval from Discord to use. Requesting them from a user without approval from Discord will lead to unexpected error behavior in the OAuth2 flow.

ValueDescriptionPublic
account.global_name.updateAllows updating the user's global nameNo
activities.invites.writeAllows sending activity invitesNo
activities.readAllows retrieving user presence and activity dataNo
activities.writeAllows updating user presence and creating headless sessionsNo
applications.builds.readAllows reading branch and build data for the user's applicationsYes
applications.builds.uploadAllows uploading builds to the user's applicationsNo
applications.commands 1Allows using commands in a guild/user contextYes
applications.commands.permissions.updateAllows updating the application's own command permissions in guilds the user has permissions inYes
applications.commands.update 2Allows your app to update its own commandsYes
applications.entitlementsAllows managing entitlements for the user's applicationsYes
applications.store.updateAllows managing store data (SKUs, store listings, achievements, etc.) for the user's applicationsYes
application_identities.writeAllows managing application identitiesNo
botAdds the application's bot to a user-selected guildYes
connectionsAllows retrieving a user's connected accounts, both public and privateYes
dm_channels.readAllows reading information about the user's DMs and group DMsNo
dm_channels.messages.readAllows reading messages from the user's DMs and group DMsNo
dm_channels.messages.writeAllows sending messages to the user's DMsNo
email 3Allows retrieving a user's email addressYes
gateway.connect 3Allows connecting to the gateway on behalf of the userNo
gdm.joinAllows adding users to managed group DMsYes
guildsAllows retrieving the user's guildsYes
guilds.channels.readAllows reading the channels in a user's guildsNo
guilds.joinAllows joining users to a guildYes
guilds.members.readAllows retrieving a user's member information in a guildYes
identifyAllows retrieving the current userYes
lobbies.writeAllows managing lobbiesNo
messages.readWhen using RPC, allows reading messages from all client channels (otherwise restricted to application-managed group DMs)Yes
openidAllows retrieving basic user information and includes an ID token in the token exchangeYes
payment_sources.country_codeAllows retrieving the user's country codeNo
presences.readAllows retrieving user presenceNo
presences.writeAllows updating user presenceNo
relationships.read 9Allows retrieving a user's relationshipsYes
relationships.writeAllows managing a user's relationshipsNo
role_connections.write 4Allows updating a user's connection and application-specific metadataYes
rpc 5 6When using RPC, allows controlling the local Discord client; also encompasses all of the below RPC scopes in the majority of scenariosNo
rpc.activities.write 6When using RPC, allows updating a user's activityYes
rpc.apiAllows accessing the REST API on behalf of the userNo
rpc.notifications.read 6When using RPC, allows you to receive notifications pushed out to the userYes
rpc.screenshare.read 6When using RPC, allows reading a user's screenshare statusYes
rpc.screenshare.write 6When using RPC, allows updating a user's screenshare settingsYes
rpc.video.read 6When using RPC, allows reading a user's video statusYes
rpc.video.write 6When using RPC, allows updating a user's video settingsYes
rpc.voice.read 6When using RPC, allows reading a user's voice settings and listening for voice eventsYes
rpc.voice.write 6When using RPC, allows updating a user's voice settingsYes
voice 3 7Allows connecting to voice on the user's behalf and seeing all voice members in a guildNo
webhook.incoming 8Creates an application-owned webhook in a user-selected channel and returns it in the token exchangeYes

1 In a user install context, this scope also allows the application to send DMs to the user.

2 Only available through the client credentials grant flow.

3 Depends on the identify scope being authorized as well.

4 Only available through the authorization code grant flow and requires that the PUBLIC_OAUTH2_CLIENT application flag is not set.

5 Unless the application is approved for general RPC access, the rpc scope is allowed for the application owner and whitelisted users only and requires that the EMBEDDED application flag is not set.

6 Access to RPC for web applications requires approval from Discord.

7 Also includes gateway.connect privileges.

8 Only available through the authorization code grant flow.

9 Requires the SOCIAL_LAYER_INTEGRATION_LIMITED or SOCIAL_LAYER_INTEGRATION application flag.

Umbrella OAuth2 Scopes

The following scopes are considered "umbrella" scopes, meaning that they are used to request access to multiple scopes at once. These are currently only used by the social layer integration.

ValueDescription
sdk.social_layer_presenceIncludes the scopes activities.invites.write, activities.read, activities.write, application_identities.write, gateway.connect, identify, relationships.read, and relationships.write
sdk.social_layerIncludes everything in sdk.social_layer_presence, plus dm_channels.read, dm_channels.messages.read, dm_channels.messages.write, guilds, guilds.channels.read, and lobbies.write
Authorization URL Structure
FieldTypeDescription
client_idsnowflakeThe ID of the application
response_type? 1stringThe type of response to return
scope? 2stringA space-delimited list of scopes to request; may be omitted if the application has a populated integration_types_config
redirect_uri? 3stringThe URL to redirect to after authorization; must match one of the registered redirect URIs for the application
prompt?stringThe prompt behavior to use for the authorization flow (default consent)
state?stringA unique string to bind the user's request to their authenticated state
nonce?stringA unique string to bind the user's request to their authenticated state; only applicable for authorization code grants with the openid scope
code_challenge?stringA code challenge for the PKCE extension to the authorization code grant; must be used with code_challenge_method
code_challenge_method?stringThe method used to generate the code challenge (must be S256); only applicable for the PKCE extension to the authorization code grant
integration_type?integerThe installation context for the authorization; only applicable when scope contains applications.commands (default GUILD_INSTALL)
permissions?integerThe permissions you're requesting; only applicable when scope contains bot
guild_id?snowflakeThe ID of a guild to pre-fill the dropdown picker with; only applicable when scope contains bot, applications.commands, or webhook.incoming and integration_type is GUILD_INSTALL
channel_id?snowflakeThe ID of a channel to pre-fill the dropdown picker with; only applicable when scope contains webhook.incoming
disable_guild_select?boolean 4Disallows the user from changing the guild dropdown; only applicable when scope contains bot or applications.commands, or webhook.incoming and integration_type is GUILD_INSTALL (default false)

1 Required unless the basic bot authorization flow is used.

2 If the bot scope is selected, the applications.commands scope is automatically added to the authorization by official clients.

3 If a response_type is specified and no redirect_uri is specified, the user will be redirected to the first registered redirect URI for the application.

4 Only accepts true or false.

Response Type
ValueDescription
codeAuthorization code grant flow
tokenImplicit grant flow
Prompt Behavior
ValueDescription
noneSkips the authorization screen and immediately redirects the user; requires previous authorization with the requested scopes
consentPrompts the user to re-approve their authorization

State and Security

Before we dive into the semantics of the different OAuth2 grants, we should stop and discuss security, specifically the use of the state parameter. Cross Site Request Forgery, or CSRF, and Clickjacking are security vulnerabilities that must be addressed by individuals implementing OAuth. This is typically accomplished using the state parameter. state is sent in the authorization request and returned back in the response and should be a value that binds the user's request to their authenticated state. For example, state could be a hash of the user's session cookie, or some other nonce that can be linked to the user's session.

When a user begins an authorization flow on the client, a state is generated that is unique to that user's request. This value is stored somewhere only accessible to the client and the user, i.e. protected by the same-origin policy. When the user is redirected, the state parameter is returned. The client validates the request by checking that the state returned matches the stored value. If they match, it is a valid authorization request. If they do not match, it's possible that someone intercepted the request or otherwise falsely authorized themselves to another user's resources, and the request should be denied.

For OpenID Connect, the nonce parameter may be used in a similar way instead. Upon receiving the ID token, the client should validate that the nonce claim in the ID token matches the nonce sent in the authorization request.

While Discord does not require the use of the state parameter, we highly recommend that you implement it for the security of your own applications and data.

Authorization Code Grant

The authorization code grant is what most developers will recognize as "standard OAuth2" and involves retrieving an access code and exchanging it for a user's access token. It allows the authorization server to act as an intermediary between the client and the resource owner, so the resource owner's credentials are never shared directly with the client.

Example Authorization URL
https://discord.com/oauth2/authorize?response_type=code&client_id=157730590492196864&scope=identify%20guilds.join&state=15773059ghq9183habn&redirect_uri=https%3A%2F%2Fnicememe.website&prompt=consent&integration_type=0

When someone navigates to this URL, they will be prompted to authorize your application for the requested scopes. On acceptance, they will be redirected to your redirect_uri, which will contain an additional query-string parameter, code. state will also be returned if previously sent, and should be validated at this point.

prompt controls how the authorization flow handles existing authorizations. If a user has previously authorized your application with the requested scopes and prompt is set to consent, it will request them to reapprove their authorization. If set to none, it will skip the authorization screen and redirect them back to your redirect URI without requesting their authorization. For passthrough scopes, like bot and webhook.incoming, authorization is always required.

The integration_type parameter specifies the installation context for the authorization. The installation context determines where the application will be installed, and is only relevant when scope contains applications.commands. When set to GUILD_INSTALL, the application will be authorized for installation to a guild. When set to USER_INSTALL, the application will be authorized for installation to a user. The application must be configured to support the provided integration_type.

Example Redirect URL
https://nicememe.website/?code=NhhvTDYsFcdgNLnnLijcl7Ku7bEEeee&state=15773059ghq9183habn

code is now exchanged for the user's access token by making a request to the token URL as follows:

Example Access Token Exchange
API_ENDPOINT = 'https://discord.com/api/v10'
CLIENT_ID = '332269999912132097'
CLIENT_SECRET = '937it3ow87i4ery69876wqire'
REDIRECT_URI = 'https://nicememe.website'
def exchange_code(code):
data = {
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET,
'grant_type': 'authorization_code',
'code': code,
'redirect_uri': REDIRECT_URI
}
headers = {
'Content-Type': 'application/x-www-form-urlencoded'
}
r = requests.post('%s/oauth2/token' % API_ENDPOINT, data=data, headers=headers)
r.raise_for_status()
return r.json()

In response, you will receive an access token response:

Example Access Token
{
"token_type": "Bearer",
"access_token": "ODkxNDM2MjMzOTAzOTY0MTYx.6qrZcUqja7812RVdnEKjpzOL4CvHBFG",
"scope": "identify",
"expires_in": 604800,
"refresh_token": "D43f5y0ahjqew82jZ4NViEr2YafMKhue"
}

Having the user's access token allows your application to make certain requests to the API on their behalf, restricted to whatever scopes were requested.

Refresh Token Grant

expires_in is how long, in seconds, until the returned access token expires, allowing you to anticipate the expiration and refresh the token. To refresh, make another request to the token URL with the following parameters:

Example Refresh Token Exchange
API_ENDPOINT = 'https://discord.com/api/v10'
CLIENT_ID = '332269999912132097'
CLIENT_SECRET = '937it3ow87i4ery69876wqire'
REDIRECT_URI = 'https://nicememe.website'
def refresh_token(refresh_token):
data = {
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET,
'grant_type': 'refresh_token',
'refresh_token': refresh_token
}
headers = {
'Content-Type': 'application/x-www-form-urlencoded'
}
r = requests.post('%s/oauth2/token' % API_ENDPOINT, data=data, headers=headers)
r.raise_for_status()
return r.json()

Boom; fresh access token response!

Implicit Grant

The implicit OAuth2 grant is a simplified flow optimized for in-browser clients. Instead of issuing the client an authorization code to be exchanged for an access token, the client is directly issued an access token. The URL is formatted as follows:

Example Authorization URL
https://discord.com/oauth2/authorize?response_type=token&client_id=290926444748734499&scope=identify&state=15773059ghq9183habn

On redirect, your redirect URI will contain additional URI fragments representing a serialiazed access token response: access_token, token_type, expires_in, scope, and state (if specified). These are not query string parameters. Be mindful of the "#" character:

Example Redirect URL
https://findingfakeurlsisprettyhard.tv/#access_token=RTfP0OK99U3kbRtHOoKLmJbOn45PjL&token_type=Bearer&expires_in=604800&scope=identify&state=15773059ghq9183habn

There are tradeoffs in using the implicit grant flow. It is both quicker and easier to implement, but rather than exchanging a code and getting a token returned in a secure HTTP body, the access token is returned in the URI fragment, which makes it possibly exposed to unauthorized parties. You also are not returned a refresh token, so the user must explicitly reauthorize once their token expires.

Client Credentials Grant

The client credential flow is a quick and easy way for bot developers to get their own bearer tokens for testing purposes. By making a request to the token URL with a grant type of client_credentials, you will be returned an access token for the bot owner. Therefore, always be super-extra-very-we-are-not-kidding-like-really-be-secure-make-sure-your-info-is-not-in-your-source-code careful with your client_id and client_secret. We don't take kindly to imposters around these parts.

You can specify scopes with the scope parameter, which is a list of OAuth2 scopes separated by spaces:

Example Client Credentials Request
API_ENDPOINT = 'https://discord.com/api/v10'
CLIENT_ID = '332269999912132097'
CLIENT_SECRET = '937it3ow87i4ery69876wqire'
def get_token():
data = {
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET,
'grant_type': 'client_credentials',
'scope': 'identify connections'
}
headers = {
'Content-Type': 'application/x-www-form-urlencoded'
}
r = requests.post('%s/oauth2/token' % API_ENDPOINT, data=data, headers=headers)
r.raise_for_status()
return r.json()

In return, you will receive an access token (without a refresh token):

Example Access Token
{
"token_type": "Bearer",
"access_token": "6qrZcUqja7812RVdnEKjpzOL4CvHBFG",
"scope": "identify connections",
"expires_in": 604800
}

Note that team-owned applications are limited to the scopes applications.builds.read, applications.builds.upload, applications.commands.update, applications.entitlements, applications.store.update, and identify. This is because these applications are owned by a pseudo-user that is not meant to be operated like a normal user account.

Device Code Grant

The device code grant is a flow that allows users to authenticate on devices that do not have a web browser or are otherwise unable to complete the OAuth2 flow in a traditional way.

To use the device code grant, you first need to make a request to the device code authorize URL to retrieve a device code and user code:

Example Device Code Request
API_ENDPOINT = 'https://discord.com/api/v10'
CLIENT_ID = '332269999912132097'
CLIENT_SECRET = '937it3ow87i4ery69876wqire'
def get_device_code():
data = {
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET,
'scope': 'identify connections'
}
headers = {
'Content-Type': 'application/x-www-form-urlencoded'
}
r = requests.post('%s/oauth2/device/authorize' % API_ENDPOINT, data=data, headers=headers)
r.raise_for_status()
return r.json()

In return, you will receive a response containing the necessary information to start the authorization process:

Example Device Code Response
{
"device_code": "PZVqIuLlME19uotfsUATN65Ytgbejbhj7Eob8qzali",
"user_code": "ZAW6C586",
"verification_uri": "https://discord.com/activate",
"verification_uri_complete": "https://discord.com/activate?user_code=ZAW6C586",
"expires_in": 300,
"interval": 5
}

You should display the user_code and verification_uri to the user, or embed the verification_uri_complete link in a QR code for them to scan.

While you are prompting the user, you should also start polling the token URL to check if the user has completed the authorization process every interval seconds or until the expires_in time has passed:

Example Device Code Exchange
API_ENDPOINT = 'https://discord.com/api/v10'
CLIENT_ID = '332269999912132097'
CLIENT_SECRET = '937it3ow87i4ery69876wqire'
def exchange_device_code(device_code):
data = {
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET,
'grant_type': 'urn:ietf:params:oauth:grant-type:device_code',
'device_code': device_code
}
headers = {
'Content-Type': 'application/x-www-form-urlencoded'
}
r = requests.post('%s/oauth2/token' % API_ENDPOINT, data=data, headers=headers)
if r.status_code == 200:
return r.json()
elif r.status_code == 400:
data = r.json()
if r['error'] == 'authorization_pending':
# The user has not yet completed the authorization process
return None
elif r['error'] == 'slow_down':
# Increase the polling interval
return None
elif r['error'] == 'expired_token':
# The device code has expired, you need to request a new one
raise Exception('The device code has expired, please request a new one')
elif r['error'] == 'access_denied':
# The user has denied the authorization request
raise Exception('The user has denied the authorization request')
r.raise_for_status()

If the user has completed the authorization process, you will receive an access token response as usual.

PKCE

Discord supports the Proof Key for Code Exchange (PKCE) extension to the OAuth2 authorization code flow.

PKCE allows users to authenticate with your application without sharing your client secret. This enables user-facing applications such as browser extensions or mobile apps to manage authentication securely. The flow runs entirely between the user and Discord, allowing users to refresh their own bearer tokens without needing your application's client secret.

If you want your clients to be able to refresh their own tokens automatically, you will need to enable the PUBLIC_OAUTH2_CLIENT application flag.

When using PKCE, your application can also utilize custom schemes in redirect_uris.

Code Verifier

Firstly, the client needs to create a code verifier, code_verifier:

  • The verifier must be a string of 43 - 128 characters.
  • The characters must be alphanumeric (A-Z, a-z, 0-9) and hyphens -, periods ., underscores _, and tildes ~.
  • The code verifier must also be randomly generated for each authorization request.

The PKCE specification recommends that you generate a 32 byte random string and base64 URL encode it without padding, resulting in a 43-byte string.

code_verifier = base64.urlsafe_b64encode(os.urandom(32)).rstrip(b'=').decode('utf-8')

Code Challenge

Next, the client needs to create a code challenge, code_challenge, which is the base64 URL-encoded SHA256 hash of the code_verifier, without padding.

SHA256 is the only supported hashing algorithm for PKCE in Discord's OAuth2 implementation.

sha256 = hashlib.sha256(code_verifier.encode('utf-8')).digest()
code_challenge = base64.urlsafe_b64encode(sha256).decode('utf-8').rstrip('=')

The client then constructs the authorization URL with the code_challenge and code_challenge_method parameters:

Example Authorization URL

https://discord.com/oauth2/authorize?response_type=code&client_id=290926444748734499&scope=identify&code_challenge=CNPVOxIUDw5vcUaWT3Gn8fjrEeZs-kMEqpk2eNzqsmQ&code_challenge_method=S256&state=15773059ghq9183habn

If successful, Discord will send you to the redirect URL with the usual authorization code. The redirect URL can safely be forwarded to the client, as the code_challenge is not sensitive information. However, it is also acceptable to keep the credentials on the server instead.

When exchanging the authorization code for an access token, the client must include the code_verifier in the request. Note that to omit the client_secret field as shown below, you must have the PUBLIC_OAUTH2_CLIENT application flag set.

Example Access Token Exchange
API_ENDPOINT = 'https://discord.com/api/v10'
CLIENT_ID = '332269999912132097'
CODE_VERIFIER = 'Qs-0Scio0ScPJDYOFy1NYsOAsj6Rb6cP-Y12N9pbwV0'
REDIRECT_URI = 'https://nicememe.website'
def exchange_code(code):
data = {
'client_id': CLIENT_ID,
'grant_type': 'authorization_code',
'code': code,
'code_verifier': CODE_VERIFIER,
'redirect_uri': REDIRECT_URI
}
headers = {
'Content-Type': 'application/x-www-form-urlencoded'
}
r = requests.post('%s/oauth2/token' % API_ENDPOINT, data=data, headers=headers)
r.raise_for_status()
return r.json()

This will give you an access token response as usual. You can now refresh the token on the client, omitting the client_secret field depending on the PUBLIC_OAUTH2_CLIENT application flag.

Bots

So, what are bot accounts?

Bot vs User Accounts

Discord's API provides a separate type of user account dedicated to automation, called a bot account. Bot accounts can be created through the applications API and do not have an email and password. Unlike the normal OAuth2 flow, bot accounts have full access to all API routes without using bearer tokens, and can connect to the Real Time Gateway. Automating normal user accounts (generally called "self-bots") outside of the OAuth2/bot API is forbidden, and can result in an account termination if found. Don't get found :)

Bot accounts have a few differences in comparison to normal user accounts, namely:

  1. Bots are added to guilds through the OAuth2 API, and cannot accept normal invites.
  2. Bots cannot have friends, nor be added to or join Group DMs.
  3. Verified bots do not have a maximum number of Guilds.
  4. Bots have an entirely separate set of Rate Limits.

Bot Authorization Flow

Bot authorization is a special server-less and callback-less OAuth2 flow that makes it easy for users to add bots to guilds. The URL you create looks similar to what we use for full stack implementation:

Example Authorization URL
https://discord.com/oauth2/authorize?client_id=157730590492196864&scope=bot&permissions=1

In the case of bots, the scope parameter should be set to bot. There's also a new parameter, permissions, which is an integer corresponding to the permission calculations for the bot. You'll also notice the absence of response_type and redirect_uri. Bot authorization does not require these parameters because there is no need to retrieve the user's access token.

When the user navigates to this page, they'll be prompted to add the bot to a guild in which they have proper permissions. On acceptance, the bot will be added. Super easy!

If you happen to already know the ID of the guild the user will add your bot to, you can provide this ID in the URL as a guild_id=GUILD_ID parameter. When the authorization page loads, that guild will be preselected in the dialog if that user has permissions to add the bot to that guild. You can use this in conjunction with the parameter disable_guild_select=true to disallow the user from picking a different guild.

If your bot is super specific to your private clubhouse, or you just don't like sharing, you can make sure integration_public is disabled on your application. If unchecked, only you can add the bot to guilds. If marked as public, anyone with your bot's ID can add it to guilds in which they have proper permissions.

Advanced Bot Authorization

Devs can extend the bot authorization functionality. You can request additional scopes outside of bot, which will prompt a continuation into a complete authorization code grant flow and add the ability to request the user's access token. If you request any scopes outside of bot or applications.commands, response_type is again mandatory.

When receiving the access code on redirect, there will be additional query-string parameters of guild_id and permissions. These parameters should only be used as hints, as they are easily faked by malicious users. To be sure of the relationship between your bot and the guild, consider enabling integration_require_code_grant on your application. Enabling it requires anyone adding your bot to a guild to go through a full OAuth2 authorization code grant flow, meaning the integration will not be created until your backend exchanges the code for a token. When you retrieve the user's access token, you'll also receive information about the guild to which your bot was added through an additional guild object in the response.

Multi-Factor Authentication Requirement

For bots with elevated permissions (permissions with a * next to them), we enforce multi-factor authentication on the owner's account when added to guilds that have guild-wide MFA enabled.

Webhooks

Discord's webhook flow is a specialized version of an authorization code implementation. In this case, the scope query-string parameter needs to include webhook.incoming:

Example Authorization URL
https://discord.com/oauth2/authorize?response_type=code&client_id=157730590492196864&scope=webhook.incoming&state=15773059ghq9183habn&redirect_uri=https%3A%2F%2Fnicememe.website

When the user navigates to this URL, they will be prompted to select a channel in which to allow the webhook. When the webhook is executed, it will post its message into this channel. On acceptance, the user will be redirected to your redirect_uri. The URL will contain the code query-string parameter which should be exchanged for an access token as usual. In return, you will receive a slightly modified token response, with an additional webhook object:

Example Access Token
{
"token_type": "Bearer",
"access_token": "GNaVzEtATqdh173tNHEXY9ZYAuhiYxvy",
"scope": "webhook.incoming",
"expires_in": 604800,
"refresh_token": "PvPL7ELyMDc1836457XCDh1Y8jPbRm",
"webhook": {
"type": 1,
"id": "347114750880120863",
"name": "Application Name Here",
"avatar": "cc7e0aa58a4224a281fbb8217b808a72",
"channel_id": "345626669224982402",
"guild_id": "290926792226357250",
"application_id": "310954232226357250",
"token": "kKDdjXa1g9tKNs0-_yOwLyALC9gydEWP6gr9sHabuK1vuofjhQDDnlOclJeRIvYK-pj_",
"url": "https://discord.com/api/webhooks/347114750880120863/kKDdjXa1g9tKNs0-_yOwLyALC9gydEWP6gr9sHabuK1vuofjhQDDnlOclJeRIvYK-pj_"
}
}

From this object, you should store the webhook.id and webhook.token. See the execute webhook documentation for how to send messages with the webhook.

Any user that wishes to add your webhook to their channel will need to go through the full OAuth2 flow, and a new webhook is created each time. If you wish to send a message to all your webhooks, you'll need to iterate over each stored id:token combination and make POST requests to each one. Be mindful of our Rate Limits!

OAuth2 Access Token Object

Access Token Structure
FieldTypeDescription
token_typestringThe type of token, always Bearer
access_tokenstringThe access token
id_token? 1stringThe ID token
scopestringThe scopes the user has authorized, separated by space
expires_inintegerDuration (in seconds) after which the token expires
refresh_token?stringThe refresh token, if applicable
guild?guild objectThe guild to which the bot was added, if applicable
webhook?webhook objectThe webhook created, if applicable

1 Only returned from the Get Provisional Account Token endpoint and when using the authorization code grant with the openid scope.

OAuth2 Authorization Object

OAuth2 Authorization Structure
FieldTypeDescription
idsnowflakeThe ID of the authorization
scopesarray[string]The scopes the user has authorized the application for
applicationpartial application objectThe authorized application
disclosures?array[integer]The application disclosures that have been acknowledged by the user

Endpoints

Returns info about the current authorization.

Response Body
FieldTypeDescription
applicationpartial application objectThe current application
scopesarray[string]The scopes the user has authorized the application for
expiresISO8601 timestampWhen the access token expires
user?partial user objectThe user who has authorized, if the user has authorized with the identify scope

Get OpenID Connect Keys

GET/oauth2/keys

Returns the JSON Web Key Set used to verify OpenID Connect ID tokens issued by Discord. This endpoint is compliant with the OpenID Connect specification.

Get OpenID User Information

GET/oauth2/userinfo

Returns OpenID user information for the current authorization. This endpoint is compliant with the OpenID Connect specification.

Response Body
FieldTypeDescription
substringThe ID of the user
email 1?stringThe user's email address
email_verified 1booleanWhether the email on this account has been verified
preferred_username 2stringThe user's username
nickname 2?stringThe user's display name
picture 2stringThe user's avatar URL
locale 2stringThe user's locale

1 Requires the email scope.

2 Requires the identify scope.

Example Response
{
"sub": "852892297661906993",
"email": "dolfies@amazing.email",
"email_verified": true,
"preferred_username": "dolfies",
"nickname": "Dolfies",
"picture": "https://cdn.discordapp.com/avatars/852892297661906993/c78ef8fb1db15a3d5f1b4c057856c5c9.png",
"locale": "en-US"
}

Get OAuth2 Device Code

POST/oauth2/device/authorize

Retrieves a device code and user code for the device code grant flow.

Form Params
FieldTypeDescription
client_id? 1snowflakeThe ID of the application
client_secret? 1 2stringThe client secret of the application
scope?stringA space-delimited list of scopes to request

1 You can also pass your client_id and client_secret as basic authentication with client_id as the username and client_secret as the password.

2 Not required if the application has the PUBLIC_OAUTH2_CLIENT application flag.

Response Body
FieldTypeDescription
device_codestringThe device code to use for the device code grant
user_codestringThe user code to display to the user for authorization
verification_uristringThe URL to display to the user for authorization
verification_uri_completestringThe complete URL to redirect the user to for authorization, including the user code
expires_inintegerThe duration (in seconds) after which the device code expires
intervalintegerThe interval (in seconds) at which to poll the token endpoint for authorization status

Get OAuth2 Device Flow

POST/oauth2/device/verify

Returns information about the OAuth2 device code grant flow.

JSON Params
FieldTypeDescription
user_codestringThe device code
Response Body
FieldTypeDescription
scopesarray[string]The scopes the user is authorizing the application for
client_idsnowflakeThe ID of the application
two_way_link_code?stringThe code to use for two-way linking
typestringThe status of the OAuth2 device flow
OAuth2 Device Flow Status
ValueDescription
pendingThe device flow is pending
grantedThe device flow is completed and an access token was granted
deniedThe device flow is completed but no access token was granted

Finish OAuth2 Device Flow

POST/oauth2/device/finish

Finishes the OAuth2 device code grant flow. Returns a 204 empty response on success.

JSON Params
FieldTypeDescription
user_codestringThe device code
resultstringThe result of the flow (only granted and denied are allowed)

Get OAuth2 Token

POST/oauth2/token

Retrieves an OAuth2 access token for the given application credentials. Implements a number of different grants. Returns an oauth2 access token object on success.

Form Params
FieldTypeDescription
grant_typestringThe type of grant to use
client_id? 1snowflakeThe ID of the application
client_secret? 1 2stringThe client secret of the application
code?stringThe authorization code to exchange for a token
code_verifier?stringThe code verifier for the PKCE extension to the authorization code grant
redirect_uri?stringThe URL to redirect to after authorization; must match one of the registered redirect URIs for the application; only applicable for authorization_code grants
refresh_token?stringThe refresh token to exchange for a new access token
device_code?stringThe device code to exchange for an access token
scope?stringA space-delimited list of scopes to request; only applicable for client_credentials grants
external_auth_type? 3stringThe type of the external authentication provider
external_auth_token? 3stringThe external authentication token

1 You can also pass your client_id and client_secret as basic authentication with client_id as the username and client_secret as the password.

2 Required if the application does not have the PUBLIC_OAUTH2_CLIENT application flag, the grant_type is client_credentials, or the grant_type is authorization_code and code_verifier is not provided.

3 Providing these fields will merge the associated provisional account with the user account that is being authenticated. See the User Merge Operation Completed Gateway event for more information.

OAuth2 Grant Type
ValueDescription
authorization_codeAuthorization code grant flow
refresh_tokenRefresh token grant flow
client_credentialsClient credentials grant flow
urn:ietf:params:oauth:grant-type:device_codeDevice code grant flow

Revoke OAuth2 Token

POST/oauth2/token/revoke

Revokes the given OAuth2 access or refresh token. Returns an empty object on success.

Form Params
FieldTypeDescription
tokenstringThe access or refresh token to revoke
client_id? 1snowflakeThe ID of the application
client_secret? 1 2stringThe client secret of the application

1 You can also pass your client_id and client_secret as basic authentication with client_id as the username and client_secret as the password.

2 Not required if the application has the PUBLIC_OAUTH2_CLIENT application flag.

Get Provisional Account Token

POST/partner-sdk/token

Retrieves an access token for a provisional account with the given credentials. Returns an OAuth2 access token object on success.

The response will only contain a refresh_token for OIDC when the application does not have the PUBLIC_OAUTH2_CLIENT application flag.

If an account isn't found but the provided external authentication token is valid, a new provisional account will be created.

Display Names

How are provisional account display names determined?

  • For bot-issued tokens, the preferred_global_name specified will be used.
  • For OIDC, a provisional account's display name will be the value of the preferred_username claim, if specified in the ID token. This field is optional and should be between 1 and 32 characters. If not specified, the user's display name will default to the user's unique username, which Discord generates on creation.
  • For Steam session tickets, the display name of the user's Steam account is used as the provisional account's display name.
  • For EOS Auth Access Tokens or ID Tokens, the name of the user's Epic account is used as the provisional account's display name. EOS Connect ID Tokens do not expose any username, and thus the game will need to configure the display name manually.
  • For Unity Services ID Tokens, the display name of the user's Unity Player Account is used as the provisional account's display name.
  • For Apple ID Tokens, the name of the user's Apple ID account is used as the provisional account's display name.
  • For PlayStation Network ID Tokens, the name of the user's PlayStation Network account is used as the provisional account's display name.

To change the display name of a provisional account, use the Modify Current User Account endpoint.

JSON Params
FieldTypeDescription
client_idsnowflakeThe ID of the application
client_secret? 1stringThe client secret of the application
external_auth_typestringThe type of the external authentication provider
external_auth_tokenstringThe external authentication token

1 Not required if the application has the PUBLIC_OAUTH2_CLIENT application flag.

External Provider Authentication Type
ValueDescription
OIDCOpenID Connect ID token
EPIC_ONLINE_SERVICES_ACCESS_TOKENAccess token for Epic Online Services (supports EOS Auth access tokens)
EPIC_ONLINE_SERVICES_ID_TOKENID token for Epic Online Services (supports both EOS Auth + Connect ID tokens)
STEAM_SESSION_TICKETA Steam authentication ticket for web generated with discord set as the identity
UNITY_SERVICES_ID_TOKENID token for Unity Auth Services
DISCORD_BOT_ISSUED_ACCESS_TOKENAn access token for a user authenticated via a bot token
APPLE_ID_TOKENID token for Apple ID
PLAYSTATION_NETWORK_ID_TOKENID token for PlayStation Network

Exchange Provisional Account Child Token

POST/partner-sdk/child-token

Exchanges a parent application token for a child application token. Returns an OAuth2 access token object on success.

JSON Params
FieldTypeDescription
child_application_idsnowflakeThe ID of the child application
parent_access_tokenstringThe parent application token (max 10240 characters)

Get Provisional Account Token with Bot

POST/partner-sdk/token/bot

Retrieves an access token for a provisional account with the given credentials. Returns an oauth2 access token object on success.

If an account isn't found, a new provisional account will be created.

JSON Params
FieldTypeDescription
external_user_idstringThe ID of the user to authenticate (max 1024 characters)
preferred_global_name?stringThe preferred global name for the user (1-32 characters)

Unmerge Provisional Account

POST/partner-sdk/provisional-accounts/unmerge

Unmerge a provisional account. Returns a 204 empty response on success.

JSON Params
FieldTypeDescription
client_idsnowflakeThe ID of the application
client_secret? 1stringThe client secret of the application
external_auth_typestringThe type of the external authentication provider
external_auth_tokenstringThe external authentication token

1 Not required if the application has the PUBLIC_OAUTH2_CLIENT application flag.

Unmerge Provisional Account with Bot

POST/partner-sdk/provisional-accounts/unmerge/bot

Unmerge a provisional account. Returns a 204 empty response on success.

JSON Params
FieldTypeDescription
external_user_idstringThe ID of the user to unmerge (max 1024 characters)

Get OAuth2 Authorizations

GET/oauth2/tokens

Returns a list of OAuth2 authorization objects.

Get Application OAuth2 Authorizations

GET/oauth2/applications/{application.id}/tokens

Returns a list of OAuth2 authorization objects for the given application ID.

Preview OAuth2 Authorization

GET/oauth2/authorize

Returns information about a possible OAuth2 authorization.

Query Params
FieldTypeDescription
client_idsnowflakeThe ID of the application
response_type? 1stringThe type of response to return
scopestringA space-delimited list of scopes to request; may be omitted if the application has a populated integration_types_config
redirect_uri? 2stringThe URL to redirect to after authorization; must match one of the registered redirect URIs for the application
state?stringA unique string to bind the user's request to their authenticated state
nonce?stringA unique string to bind the user's request to their authenticated state; only applicable for authorization code grants with the openid scope
code_challenge?stringA code challenge for the PKCE extension to the authorization code grant; must be used with code_challenge_method
code_challenge_method?stringThe method used to generate the code challenge (must be S256); only applicable for the PKCE extension to the authorization code grant
integration_type?integerThe installation context for the authorization; only applicable when scope contains applications.commands (default GUILD_INSTALL)

1 Required unless the basic bot authorization flow is used.

2 If a response_type is specified and no redirect_uri is specified, the response will contain the first redirect URI registered for the application.

Response Body
FieldTypeDescription
applicationpartial application objectThe application that is being authorized
userpartial user objectThe user who is authorizing
authorizedbooleanWhether the user has already authorized the application with these scopes, meaning consent can be skipped
integration_typeintegerThe installation context for the authorization
redirect_uri??stringThe URL to redirect to after authorization; only present if response_type is specified
bot?partial user objectThe bot user that will be added to the guild; only present the bot scope is requested
guilds?array[oauth2 guild object]The user's guilds; only present if integration_type is GUILD_INSTALL and the bot or applications.commands scope is requested
OAuth2 Guild Structure
FieldTypeDescription
idsnowflakeThe ID of the guild
namestringThe name of the guild (2-100 characters)
icon?stringThe guild's icon hash
mfa_levelintegerRequired MFA level for administrative actions within the guild
permissionsstringPermissions the user has in the guild
Example OAuth2 Guild
{
"id": "81384788765712384",
"name": "Discord API",
"icon": "a363a84e969bcbe1353eb2fdfb2e50e6",
"mfa_level": 1,
"permissions": "1095530297282240"
}

Create OAuth2 Authorization

POST/oauth2/authorize

Authorizes the user for the given OAuth2 application. May fire a Guild Integrations Update, Integration Create, User Application Update, and/or User Connections Update Gateway event.

Query Params
FieldTypeDescription
client_idsnowflakeThe ID of the application
response_type? 1stringThe type of response to return
scopestringA space-delimited list of scopes to request; may be omitted if the application has a populated integration_types_config
redirect_uri? 2stringThe URL to redirect to after authorization; must match one of the registered redirect URIs for the application
state?stringA unique string to bind the user's request to their authenticated state
nonce?stringA unique string to bind the user's request to their authenticated state; only applicable for authorization code grants with the openid scope
code_challenge?stringA code challenge for the PKCE extension to the authorization code grant; must be used with code_challenge_method
code_challenge_method?stringThe method used to generate the code challenge (must be S256); only applicable for the PKCE extension to the authorization code grant

1 Required unless the basic bot authorization flow is used.

2 If a response_type is specified and no redirect_uri is specified, the user will be redirected to the first registered redirect URI for the application.

JSON Params
FieldTypeDescription
authorize?booleanWhether to authorize the user for the application (default false)
integration_type?integerThe installation context for the authorization (default GUILD_INSTALL)
permissions? 1stringThe permissions to request for the bot user in the guild; only applicable when the bot scope is requested
guild_id?snowflakeThe ID of the guild to which the application should be added or the webhook should be created; only applicable when the bot, applications.commands, or webhook.incoming scope is requested and integration_type is GUILD_INSTALL
webhook_channel_id?snowflakeThe ID of the channel where the webhook should be created; only applicable when the webhook.incoming scope is requested
dm_settings?application DM settings objectThe DM settings for the application; only applicable when the applications.commands scope is requested and integration_type is USER_INSTALL
location_context?oauth2 location context objectThe location context of the authorization within the client, used for analytics

1 If no permissions are requested, an integration role is not created for the bot user in the guild.

Application DM Settings Structure
FieldTypeDescription
allow_mobile_push?booleanWhether to allow mobile push notifications for the application's DMs (default false)
OAuth2 Location Context Structure
FieldTypeDescription
guild_id?snowflakeThe ID of the guild where the authorization is being created
channel_id?snowflakeThe ID of the channel where the authorization is being created
channel_type?integerThe type of channel the authorization is being created in
Response Body
FieldTypeDescription
url 1stringThe URL to redirect the user to, containing the authorization code, access token, or error

1 If a redirect_uri doesn't exist, https://discord.com will be used.

Get OAuth2 Authorization

GET/oauth2/tokens/{token.id}

Returns an OAuth2 authorization for the given ID.

Delete OAuth2 Authorization

DELETE/oauth2/tokens/{token.id}

Revokes the given authorization. Returns a 204 empty response on success. Fires multiple OAuth2 Token Revoke and optionally a User Application Remove and User Connections Update Gateway event.