# Building Your First Activity in Discord
Source: https://docs.discord.com/developers/activities/building-an-activity
Step-by-step tutorial for creating your first Discord Activity.
[Activities](/developers/activities/overview) are web-based games and apps that can be run within Discord. Activities are embedded in iframes within the Discord client, and can be launched from the App Launcher or when responding to interactions.
If this is your first time learning about Activities, check out the [Activities Overview](/developers/activities/overview) for more information and a collection of more advanced [sample projects](/developers/activities/overview#sample-projects).
## Introduction
In this guide, we'll be building a Discord app with a basic Activity that handles user authentication and fetches data using the API.
It assumes an understanding of [JavaScript](https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/JavaScript_basics) and [async functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function), and a basic understanding of frontend frameworks like [React](https://react.dev/) and [Vue](https://vuejs.org/). If you are still learning to program, there are many free education resources to explore like [The Odin Project](https://www.theodinproject.com/paths/foundations/courses/foundations), [Codecademy](https://www.codecademy.com/learn/paths/web-development), and [Khan Academy](https://www.khanacademy.org/computing/computer-programming/programming).
* **[discord/getting-started-activity](https://github.com/discord/getting-started-activity)**, a project template to get you started
* **[@discord/embedded-app-sdk](https://github.com/discord/embedded-app-sdk)**, the SDK used to communicate between your app and Discord when building Activities
* **[Node.js](https://nodejs.org)**, latest version
* **[Express](https://expressjs.com)**, a popular JavaScript web framework we'll use to create a server to handle authentication and serve our app
* **[Vite](https://vite.dev/)**, a build tool for modern JavaScript projects that will make your application easier to serve
* **[cloudflared](https://github.com/cloudflare/cloudflared?tab=readme-ov-file#installing-cloudflared)**, for bridging your local development server to the internet
***
## Step 0: Enable Developer Mode
Before getting started, you need to enable Developer Mode for your Discord account if you don't already have it enabled. Developer Mode will allow you to run in-development Activities and expose resource IDs (like users, channels, and servers) in the client which can simplify testing. To enable Developer Mode:
1. Go to your **User Settings** in your Discord client. On Desktop, you can access **User Settings** by clicking on the cogwheel icon near the bottom-left, next to your username.
2. Click on **Advanced** tab from the left-hand sidebar and toggle on `Developer Mode`.
## Step 1: Setting up the project
Before creating an app, let's set up our project code from the [`discord/getting-started-activity`](https://github.com/discord/getting-started-activity) repository.
Open a terminal window and clone the project code:
```
git clone git@github.com:discord/getting-started-activity.git
```
The sample project you cloned is broken into two parts:
* `client` is the sample Activity's frontend, built with vanilla JavaScript and integrated with [Vite](https://vitejs.dev/) to help with local development.
* `server` is a backend using vanilla JavaScript, Node.js, and Express. However, as you're building your own Activity, you can use whichever backend you prefer.
```
├── client
├────── main.js -> your Activity frontend
├────── index.html
├────── package.json
├────── rocket.png
├────── vite.config.js
├── server
├────── package.json
├────── server.js -> your Activity backend
└── .env -> your credentials, IDs and secrets
```
### Install project dependencies
Before creating our Discord app, let's quickly install your project's frontend dependencies.
Navigate to your project folder's `client` directory, which is where all the sample Activity's frontend code lives:
```
cd getting-started-activity/client
```
Then install the project's dependencies and start up the frontend for the sample Activity:
```
# install project dependencies
npm install
# start frontend
npm run dev
```
If you visit [http://localhost:5173/](http://localhost:5173/) you should see a vanilla JS frontend template running with [Vite](https://vitejs.dev/).
While it's not much at the moment, in the following steps we'll connect it to the backend services, make it runnable in Discord, and power it up by populating it with data we pull from Discord APIs.
By the end of Step 1, you should have:
* An understanding of what Discord [Activities](/developers/activities/overview) are
* Developer Mode enabled on your Discord account
* Cloned the [sample project](https://github.com/discord/getting-started-activity) to your development environment
* Installed the front-end dependencies (in the `client` folder)
***
## Step 2: Creating an app
With our project set up, let's create our app and configure the Activity. Create a new app in the developer portal if you don't have one already:
Create App
Enter a name for your app, select a development team, then press **Create**.
**Development Team Access**
Launching a non-distributed Activity is limited to you or members of the developer team, so if you're collaborating with others during development, create a [developer team](https://discord.com/developers/teams) and set it to the owner when you create the app.
After you create your app, you'll land on the **General Information** page of the app's settings, where you can update basic information about your app like its description and icon.
### Choose installation contexts
Apps in Discord can be installed to different **[installation contexts](/developers/resources/application#installation-context)**: servers, user accounts, or both.
The recommended *and* default behavior for apps is supporting both installation contexts, which lets the installer to choose the context during the installation flow. However, you can change the default behavior by changing the supported installation contexts in your app's settings.
As mentioned, installation contexts determine where your app can be installed. The installation context affect things like who can manage the installation, where the app's commands can appear, and the data returned in response to interactions.
* Apps installed in a **[server context](/developers/resources/application#server-context)** (server-installed apps) must be authorized by a server member with the `MANAGE_GUILD` permission, and are visible to all members of the server.
* Apps installed in a **[user context](/developers/resources/application#user-context)** (user-installed apps) are visible only to the authorizing user, and therefore don't require any server-specific permissions. Apps installed to a user context are visible across all of the user's servers, DMs, and GDMs—however, they're limited to using commands.
Details about installation contexts is in the [Application documentation](/developers/resources/application#installation-context) and the [Developing a User-Installable App tutorial](/developers/tutorials/developing-a-user-installable-app).
Click on [**Installation**](https://discord.com/developers/applications/select/installation) in the left sidebar, then under **Installation Contexts** make sure both "User Install" and "Guild Install" are selected. This will make sure users can launch our app's Activity across Discord servers, DMs, and Group DMs.
### Add a Redirect URI
Next, we'll add a Redirect URI, which is where a user is typically redirected to after authorizing with your app when going through the standard OAuth flow. While setting up a Redirect URI is required, the Embedded App SDK automatically handles redirecting users back to your Activity when the RPC [`authorize` command](/developers/developer-tools/embedded-app-sdk#authorize) is called.
You can learn more about the OAuth flow and redirect URIs in the [OAuth2 documentation](/developers/topics/oauth2), but since we're only authorizing in an Activity, we'll just use a placeholder value (`https://127.0.0.1`) and let the Embedded App SDK handle the rest.
Click on [**OAuth2**](https://discord.com/developers/applications/select/oauth2) in your app's settings. Under **Redirects**, enter `https://127.0.0.1` as a placeholder value then click **Save Changes**.
### Fetch Your OAuth2 Credentials
To use information related to a user (like their username) or a server (like the server's avatar), your app must be granted specific OAuth **scopes**.
For our sample app, we'll be requesting three scopes: `identify` to access basic information about a user, `guilds` to access basic information about the servers a user is in, and `applications.commands` to install [commands](/developers/interactions/overview#commands). We'll request these later on in the guide, but a full list of scopes you can request is in the [OAuth2 documentation](/developers/topics/oauth2#shared-resources-oauth2-scopes).
When requesting scopes later on, you'll need to pass your app's OAuth2 identifiers. For now, we'll copy these identifiers into your project's environment file.
In the root of your project, there is an `example.env` file. From the root of your project, run the following to copy it into a new `.env` file:
```
cp example.env .env
```
**Secure Your Secrets**
Your `DISCORD_CLIENT_SECRET` and `DISCORD_BOT_TOKEN` are *highly* sensitive secrets. Never share either secrets or check them into any kind of version control.
Back in your app's settings, click on [**OAuth2**](https://discord.com/developers/applications/select/oauth2):
1. **Client ID**: Copy the value for Client ID and add it to your `.env` file as **`VITE_CLIENT_ID`**. This is the public ID that Discord associates with your app, and is almost always the same as your App ID.
2. **Client Secret**: Copy the value for Client Secret and add it to your `.env` as **`DISCORD_CLIENT_SECRET`**. This is a private, sensitive identifier that your app will use to grant an OAuth2 `access_token`, and should never be shared or checked into version control.
**Why is there a VITE\_ prefix before our Client ID?**
Prefixing the `CLIENT_ID` environment variable with `VITE_` makes it accessible to our client-side code. This security measure ensures that only the variables you intend to be accessible in the browser are available, and all other environment variables remain private. You can read details in the [Vite documentation](https://vitejs.dev/guide/env-and-mode).
By the end of Step 2, make sure you have:
* Set up a placeholder Redirect URI
* Added your app's Client ID and Client Secret to your project's `.env` file.
## Step 3: Setting Up the Embedded App SDK
With our project and app set up, we're going to install and configure the [Embedded App SDK](/developers/developer-tools/embedded-app-sdk) which we'll use extensively through the rest of this guide.
The Embedded App SDK is a first-party SDK that handles the communication between Discord and your Activity with [commands](/developers/developer-tools/embedded-app-sdk#sdk-commands) to interact with the Discord client (like fetching information about the channel) and [events](/developers/developer-tools/embedded-app-sdk#sdk-events) to listen for user actions and changes in state (like when a user starts or stops speaking).
The events and commands available in the Embedded App SDK are a subset of the [RPC API](/developers/topics/rpc) ones, so referencing the RPC documentation can be helpful to understand what's happening under the hood when developing Activities.
### Install the SDK
Back in our project's `client` directory from before (`getting-started-activity/client`), install the Embedded App SDK [via NPM](https://www.npmjs.com/package/@discord/embedded-app-sdk):
```
npm install @discord/embedded-app-sdk
```
This will add `@discord/embedded-app-sdk` to `getting-started-activity/client/package.json` and install the SDK in your `node_modules` folder.
### Import the SDK in your Project
Once installed, we need to import it into our client code and instantiate it to start the handshake between our app and the Discord client.
To instantiate the SDK, we will use the environment variables we set up in Step 2.
We also set up a check for the [`ready` event](/developers/developer-tools/embedded-app-sdk#ready) with an async/await function which allows us to output a log or perform other actions once the handshake was successful.
In `getting-started-activity/client/main.js`, let's import and instantiate the SDK:
```js theme={"system"}
// Import the SDK
import { DiscordSDK } from "@discord/embedded-app-sdk";
import "./style.css";
import rocketLogo from '/rocket.png';
// Instantiate the SDK
const discordSdk = new DiscordSDK(import.meta.env.VITE_DISCORD_CLIENT_ID);
setupDiscordSdk().then(() => {
console.log("Discord SDK is ready");
});
async function setupDiscordSdk() {
await discordSdk.ready();
}
document.querySelector('#app').innerHTML = `
Hello, World!
`;
```
**Time to leave your browser behind**
Once you add the SDK to your app, you will *not* be able to view your app inside your web browser. In the next step, we will run your Activity inside of Discord. In the next step, we will go over how to view your app in Discord.
By the end of Step 3, make sure you have:
* Installed the Embedded App SDK to your project
* Imported the SDK in your project's `client/main.js` file
***
## Step 4: Running your app in Discord
Let's ensure everything is wired up correctly, enable activities via the dev portal, and then run the Activity in Discord.
### Run your app
First, we'll restart the sample app. Open a terminal window and navigate to your project directory's `client` folder, then start the client-side app:
```
cd client
npm run dev
```
Your app should start and you should see output similar to the following:
```
VITE v5.0.12 ready in 100 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
➜ press h + enter to show help
```
We'll use the Local URL as our publicly-accessible URL in the next step.
### Set up a public endpoint
Next, we'll need to set up the public endpoint that serves the Activity's frontend. To do that, we'll create a tunnel with a reverse proxy. While we'll be using [`cloudflared`](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/) in this guide, you can use [ngrok](https://ngrok.com/docs) or another reverse proxy solution if you prefer.
While your app is still running, open another terminal window and start a network tunnel that listens to the port from the last step (in this case, port `5173`):
```
cloudflared tunnel --url http://localhost:5173
```
When you run `cloudflared`, the tunnel will generate a public URL and you'll see output similar to the following:
```
Your quick Tunnel has been created! Visit it at (it may take some time to be reachable):
https://funky-jogging-bunny.trycloudflare.com
```
Copy the URL from the output, as we'll need to add it to our app's settings.
### Set up your Activity URL Mapping
Because Activities are in a sandbox environment and go through the Discord proxy, you'll need to add a public URL mapping to serve your application and make external requests in your Activity. Since we're developing locally, we'll use the public endpoint we just set up.
Back in your app's settings, click on the [**URL Mappings** page](https://discord.com/developers/applications/select/embedded/url-mappings) under **Activities** on the left-hand sidebar. Enter the URL you generated from `cloudflared` in the previous step.
| PREFIX | TARGET |
| ------ | --------------------------------------- |
| `/` | `funky-jogging-bunny.trycloudflare.com` |
Read details about URL Mapping [in the development guide](/developers/activities/development-guides/local-development#url-mapping).
### Enable Activities
Next, we'll need to enable Activities for your app. On the left hand sidebar under **Activities**, click [**Settings**](https://discord.com/developers/applications/select/embedded/settings).
Find the first checkbox, labeled `Enable Activities`. Turn it on 🎉
#### Default Entry Point Command
When you enable Activities for your app, a [default Entry Point command](/developers/interactions/application-commands#default-entry-point-command) called "Launch" is automatically created. This [Entry Point command](/developers/interactions/application-commands#entry-point-commands) is the primary way for users to launch your Activity in Discord.
By default, interactions with this command will result in Discord opening your Activity for the user and posting a message in the channel where it was launched from. However, if you prefer to handle the interactions in your app, you can update the [`handler` field](/developers/interactions/application-commands#entry-point-handlers) or create your own. Additional details are in the Entry Point command [documentation](/developers/interactions/application-commands#entry-point-commands) and [development guide](/developers/activities/development-guides/user-actions#setting-up-an-entry-point-command).
### Running your Activity in Discord
Now that we are pointing Discord to our locally running app, we can launch the Activity in Discord!
Navigate to your Discord test server and, in any voice and or text channel, open the App Launcher where your in-development Activity should be present. If you don't see your Activity, you should try searching for its name.
Clicking on your app will launch your locally running app from inside Discord!
**Customizing your Activity**
If you'd like to set images for your Activity, you can learn how to do that [here](/developers/activities/development-guides/assets-and-metadata#setting-up-activity-metadata).
We're looking pretty good so far, but we haven't wired up any Discord functionality yet. Let's do that next.
By the end of Step 4, make sure you have:
* Set up a public endpoint
* Added an Activity URL Mapping in your app's settings
* Enabled Activities for your app
* Successfully launched your Activity in Discord
***
## Step 5: Authorizing & authenticating users
To authenticate your Activity with the users playing it, we must finish implementing our server-side app and get it talking to the client-side app.
We will use `express` for this example, but any backend language or framework will work here.
This diagram illustrates the common pattern for granting a user an OAuth2 access\_token:
We will be implementing this pattern in this tutorial, but more example implementations can also be found in this sample project:
* [Back-end code](https://github.com/discord/embedded-app-sdk-examples/blob/main/discord-activity-starter/packages/server/src/app.ts)
* [Front-end code](https://github.com/discord/embedded-app-sdk-examples/blob/main/discord-activity-starter/packages/client/src/main.ts)
```
# move into our server directory
cd server
# install dependencies
npm install
```
We aren't going to edit the server code here, but it consists of a single POST route for `/api/token` that allows us to perform the OAuth2 flow from the server securely.
In the `getting-started-activity/server/server.js` file, the following code should already be present:
```javascript theme={"system"}
import express from "express";
import dotenv from "dotenv";
import fetch from "node-fetch";
dotenv.config({ path: "../.env" });
const app = express();
const port = 3001;
// Allow express to parse JSON bodies
app.use(express.json());
app.post("/api/token", async (req, res) => {
// Exchange the code for an access_token
const response = await fetch(`https://discord.com/api/oauth2/token`, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
client_id: process.env.VITE_DISCORD_CLIENT_ID,
client_secret: process.env.DISCORD_CLIENT_SECRET,
grant_type: "authorization_code",
code: req.body.code,
}),
});
// Retrieve the access_token from the response
const { access_token } = await response.json();
// Return the access_token to our client as { access_token: "..."}
res.send({access_token});
});
app.listen(port, () => {
console.log(`Server listening at http://localhost:${port}`);
});
```
Now, start the project's backend server:
```
npm run dev
```
You should output similar to the following:
```
> server@1.0.0 dev
> node server.js
Server listening at http://localhost:3001
```
We can now run our server and client-side apps in separate terminal windows. You can see other ways to set this up in the other [sample projects](/developers/activities/overview#sample-projects).
### Calling external resources from your activity
Before we call your backend activity server, we need to be aware of the Discord proxy and understand how to avoid any Content Security Policy (CSP) issues.
Learn more about this topic in the guides for [Constructing a Full URL](/developers/activities/development-guides/networking#construct-a-full-url) and [Using External Resources](/developers/activities/development-guides/networking#using-external-resources).
### Calling your backend server from your client
We're almost there! Now, we need our client application to communicate with our server so we can start the OAuth process and get an access token.
**What is vite.config.js?**
To allow our frontend app to call our Express server, Vite requires us to set up a proxy for `/api/*` to our backend server, which is running on port 3001. In their docs, you can learn more about [Vite](https://vitejs.dev/).
Copy the following code in your project's `getting-started-activity/client/main.js` file:
```javascript theme={"system"}
import { DiscordSDK } from "@discord/embedded-app-sdk";
import rocketLogo from '/rocket.png';
import "./style.css";
// Will eventually store the authenticated user's access_token
let auth;
const discordSdk = new DiscordSDK(import.meta.env.VITE_DISCORD_CLIENT_ID);
setupDiscordSdk().then(() => {
console.log("Discord SDK is authenticated");
// We can now make API calls within the scopes we requested in setupDiscordSDK()
// Note: the access_token returned is a sensitive secret and should be treated as such
});
async function setupDiscordSdk() {
await discordSdk.ready();
console.log("Discord SDK is ready");
// Authorize with Discord Client
const { code } = await discordSdk.commands.authorize({
client_id: import.meta.env.VITE_DISCORD_CLIENT_ID,
response_type: "code",
state: "",
prompt: "none",
scope: [
"identify",
"guilds",
"applications.commands"
],
});
// Retrieve an access_token from your activity's server
const response = await fetch("/api/token", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
code,
}),
});
const { access_token } = await response.json();
// Authenticate with Discord client (using the access_token)
auth = await discordSdk.commands.authenticate({
access_token,
});
if (auth == null) {
throw new Error("Authenticate command failed");
}
}
document.querySelector('#app').innerHTML = `
Hello, World!
`;
```
Now if we relaunch our app, we'll be prompted to authorize with Discord using the `identify`, `guilds`, and `applications.commands` scopes.
**Safe storage of tokens**
Access tokens and refresh tokens are powerful, and should be treated similarly to passwords or other highly-sensitive data. Store both types of tokens securely and in an encrypted manner.
By the end of Step 5, make sure you have:
* Updated your `client/main.js` to call the backend to support user authorization and authentication
* Been able to successfully complete the authorization flow for your app when opening your Activity
***
## Step 6: Use the SDK to fetch the channel
Now that we have authenticated our users, we can start interacting with contextual Discord information that we can use in our application.
Let's use the SDK to get details about the channel that our activity is running in. We can do this by writing a new async function that uses the `commands.getChannel` SDK method.
In the same `getting-started-activity/client/main.js` file, paste the following function:
```javascript theme={"system"}
async function appendVoiceChannelName() {
const app = document.querySelector('#app');
let activityChannelName = 'Unknown';
// Requesting the channel in GDMs (when the guild ID is null) requires
// the dm_channels.read scope which requires Discord approval.
if (discordSdk.channelId != null && discordSdk.guildId != null) {
// Over RPC collect info about the channel
const channel = await discordSdk.commands.getChannel({channel_id: discordSdk.channelId});
if (channel.name != null) {
activityChannelName = channel.name;
}
}
// Update the UI with the name of the current voice channel
const textTagString = `Activity Channel: "${activityChannelName}"`;
const textTag = document.createElement('p');
textTag.textContent = textTagString;
app.appendChild(textTag);
}
```
Now, update the callback after `setupDiscordSdk()` to call the function you just added:
```javascript theme={"system"}
setupDiscordSdk().then(() => {
console.log("Discord SDK is authenticated");
appendVoiceChannelName();
});
```
If you close and rejoin the Activity, you should now see the name of the current channel.
By the end of Step 6, make sure you have:
* Updated your `client/main.js` code to fetch the channel name using the SDK
* Added a call to the new function in the callback for `setupDiscordSdk()`
***
## Step 7: Use the API to fetch the guild
Since we requested the `identify` and `guilds` scopes, you can also use the authorized `access_token` we received earlier to fetch those resources via the API.
In the following code block, we will:
1. Call the [`GET /users/@me/guilds`](/developers/resources/user#get-current-user-guilds) endpoint with `auth.access_token` to get a list of the guilds the authorizing user is in
2. Iterate over each guild to find the guild we are in based on the `guildId` defined in discordSdk
3. Create a new HTML image element with the guild avatar and append it to our frontend
In this example, we use a pure `fetch` request to make the API call, but you can us one of the JavaScript [community-built libraries](/developers/developer-tools/community-resources) if you prefer.
In the same `client/main.js` file, add the following function:
```javascript theme={"system"}
async function appendGuildAvatar() {
const app = document.querySelector('#app');
// 1. From the HTTP API fetch a list of all of the user's guilds
const guilds = await fetch(`https://discord.com/api/v10/users/@me/guilds`, {
headers: {
// NOTE: we're using the access_token provided by the "authenticate" command
Authorization: `Bearer ${auth.access_token}`,
'Content-Type': 'application/json',
},
}).then((response) => response.json());
// 2. Find the current guild's info, including it's "icon"
const currentGuild = guilds.find((g) => g.id === discordSdk.guildId);
// 3. Append to the UI an img tag with the related information
if (currentGuild != null) {
const guildImg = document.createElement('img');
guildImg.setAttribute(
'src',
// More info on image formatting here: https://docs.discord.com/developers/reference#image-formatting
`https://cdn.discordapp.com/icons/${currentGuild.id}/${currentGuild.icon}.webp?size=128`
);
guildImg.setAttribute('width', '128px');
guildImg.setAttribute('height', '128px');
guildImg.setAttribute('style', 'border-radius: 50%;');
app.appendChild(guildImg);
}
}
```
Then, call the new function in the callback for `setupDiscordSdk`:
```javascript theme={"system"}
setupDiscordSdk().then(() => {
console.log("Discord SDK is authenticated");
appendVoiceChannelName();
appendGuildAvatar();
});
```
If we relaunch our Activity, we will see the current server's avatar render in our Activity.
At this point, you should have your Activity up and running. For Step 7, you should have:
* Updated your `client/main.js` code to fetch the guild information using the [`GET /users/@me/guilds`](/developers/resources/user#get-current-user-guilds) API endpoint
* Added a call to the new function in the callback for `setupDiscordSdk()`
***
## Next Steps
Congrats on building your first Activity! 🎉
This is an intentionally simple example to get you started with the communication between your Activity and Discord using the Embedded App SDK and APIs. From here, you can explore the [Activities documentation](/developers/activities/overview) and other resources.
}>
Follow our Activities Development Guides for suggested development practices and considerations.
}>
Try out the full range of Embedded App SDK features in the playground app, or explore some of the other examples
}>
Join our community to ask questions about the API, attend events hosted by the Discord platform team, and interact with other Activities developers
# Activity Design Patterns
Source: https://docs.discord.com/developers/activities/design-patterns
Best practices and design principles for creating engaging Discord Activities.
Before building your Activity, here are some guiding principles for you to consider for a successful user experience.
## Guiding Principles
### Interaction over Isolation
* Activities are not for screen-sharing, we already have that built-in!
* Give users a way to interact with one another.
* User actions should impact the experiences of other participants.
### Expression over Monotony
* Our users want to create memorable moments.
* Create experiences where users craft moments and react in ways they want to share with others.
### Accessibility over Exclusion
* Inclusivity is key.
* Reduce the bar to entry for your Activity and reward people for engaging deeply with your experience.
***
## Providing a "Solo-Plus" Experience
Successful experiences will be built to accommodate small group sessions ("Plus"), but also playable and enjoyable for the case of single-player ("Solo") sessions.
### Solo or single-player
* While most people use Discord with small groups of friends. Consider experiences that are compelling alone, but better together!
* If you don't support solo play, expect to see a lot of users peeking into the Activity on their own. Consider what you can do to preview the Activity and compel them to come back with their friends.
### Solo-Plus or small groups of users
* Small group sessions (3-8 people) show more engagement and retention from users than single-player experiences.
* Letting players join with their friends keeps them coming back for more.
### Large groups of users
* While you can set a "max participants" suggestion to users, the only real limit is the number of people who can join a Voice call.
* Be aware of how your Activity will behave when there are 25 or more people in the call.
***
## User Presence & Privacy
When in an Activity with others, make the actions and presence of the others visible to each player.
### Make actions and presence of the others visible to each player
* Users enjoy participating in a space that feels active.
* If a user has customized their server nickname or avatar, use their server nickname or avatar in game.
* Show when a user is speaking in the voice call, or whether they're active or inactive.
### Respect user privacy
* For Activity sessions that match users from various servers, usernames and avatars should be anonymized by default.
* Discord personal identifiers should only be populated into the Activity with user confirmation.
* Only use these identifiers when the people who can see them already have access via the server or DM.
***
## Deliver a Quality Experience
Make your app fast, easy to join, and maximize fun to launch a crowd favorite.
### Surprise and delight users
* Surprise and delight is about caring about the small details of how a person experiences your work.
* Put the right emotion in when they least expect it to deliver the magic.
### Keep load times as low as possible
* This allows for easier drop-in drop-out behavior for the large portion of mobile users on Discord.
* See the below [Quality & Testing Recommendations](/developers/activities/design-patterns#quality-and-testing) for key areas of minimum quality support and testing recommendations.
* See the below [Technical Considerations](/developers/activities/design-patterns#technical-considerations) for recommendations on how to partition loading and work with various development tools to reduce load times.
* Consider different screen sizes and orientations across desktop and mobile devices and make sure UI elements scale appropriately.
### Support drop-in, drop-out behavior
* Activities are frictionless to join and easy to discover, so you can expect that users will join mid-experience. Give those users something to do, even if it's just letting them spectate until they can join without being disruptive. In the same vein, users can leave without notice or become afk (away from keyboard). Handle these cases gracefully.
* Create a case for users who have joined a call but have not yet started playing or engaging. Allow these users to "spectate" other users who are playing. This can also be helpful for Activities that have an ideal number in mind for play.
### Make your app as available as possible
#### Cross-Platform Support
* Supporting desktop and mobile devices will expand your user-base to the widest number of users.
* Discord device usage is split between mobile and desktop platforms.
#### Discord is a Global Audience
* Consider supporting a multitude of languages and cultures to make your app more usable for all Discord users.
#### Implement Invites
* The Embedded App SDK allows for sending invites from within an Activity out to other friended users.
* Add invite buttons to a screen or flow with intentionality, not only on the start screen and at the beginning. Examples of intentional Invite prompting include:
* Cases where players leave the activity session
* Cases where the minimum number of participants has not been reached (e.g. any activity that needs 2+ to start, or is more fun with more people, or when you need an opponent in a game like Chess.)
#### Implement Sharing
Discord is a social platform where users talk to each other. Sharing and invites lets your app live and engage in those shared spaces, making it visible and accessible to everyone not currently playing. Things like sharing and invites are important ways to reach into those private, shared spaces and attract other players.
* The Embedded App SDK allows for sending an image or gif from within an Activity.
* Share photos or GIFs that capture moments of fun and memorable, or something to brag about. Don't make things shareable just to feature the activity.
* Sharing a high score alone may not be very engaging, but sharing a really good move made in a game, or a collaborative drawing that creates a memory is a conversation starter and may make others want to join in on the fun.
#### Activities in Text Channels
* The Activity user interface, copy and user flows should not rely on people in voice to explain, organize, clarify, or instruct about how the activity works.
* All controls, CTAs, and instructions should be clear enough that folks (especially first time users) playing in a text channel are able to quickly start using the app without needing to talk on voice to learn about it.
* Lean on dynamic first-time user experience and "How To Play" instructions, toast messages, Call To Action buttons, etc. but be careful to not over-clutter with copy.
### Monetization Considerations
Monetization in Activities will be available soon. Keep the following considerations in mind as you design your Activity.
* Avoid prohibitive gates in front of participation (e.g. login wall / paywall), especially early in the user journey.
* Avoid monetized unlocks that give unfair advantage to other non-paying players or users (i.e. "pay to win").
* Take advantage of Discord as a social platform: look for opportunities to offer users customizable skins, aesthetic themes, new avatars, etc.
***
## Game Design Considerations
### Build games that are easy to learn, hard to master
* Players can often feel pressure when joining a multiplayer game (voice/video) in the moment if they’ve never played before.
* Games that have a simple quick reference control scheme that are easily approachable help ease players into an experience without dropping them into the deep end.
* An easy to understand game loop combined with easy to digest controls tends to work best. If you have to go through a tutorial to understand the game then it will be intimidating to join a game in the moment.
### Discord users who engage with voice are among our most engaged users
* Activity engagement and retention increases when users are in a voice call with friends.
### Consider existing game expectations and tropes
* If you're developing a game, you're more likely to attract users who are gamers.
* Take advantage of existing tropes and expectations for games of the genre you're developing.
* Appeal to the player interests that your game (or game genre) provides, and support those interests as a means to get them coming back.
### Activities close when the last participant in the Activity leaves it
* The next time the Activity is launched, even if it’s in the same DM or Voice Channel, it will be a different instance and could have different participants.
### If a game is launched, what % of the time do users reach different phases of the game?
* This includes launch → start, as there can be a large dropoff between these two (especially if you don’t support solo play or small group sizes).
* This also includes various check points in the game, including what % of sessions that start a game reach the end of that game!
* If your Activity has different settings you can start a game with, see if some are more popular or more successful — you may want to change your defaults!
* For non-games, you may want to analyze a certain action instead (such as queueing a video / song for a co-watching / co-listening Activity).
* If drop-off is really high at a certain point, see if you can figure out why or change flows.
### How many games are played in a session?
* More games per session isn’t inherently better (you may have an Activity that is meant to be one long game), but is a good baseline to understand.
* If you expect to see a lot of repeat plays per session and don’t, it can be worth digging in to understand more.
### How does the group size impact various key metrics?
* For example, are larger sessions more or less likely to reach the end of a game? To replay? Etc.
* This can help you catch if your Activity has unexpected weak points in different group sizes — maybe the game drags on if there are too many people or isn’t compelling enough if there’s only two.
* Not every Activity needs to be built for robust group sizes, but if you have the option to play with X # of players, it’s good practice to make sure that experience is enjoyable for all involved!
***
## Co-Watching / Co-Listening
* Co-watching and co-listening should be more than just screen-sharing. Consider creative ways to make each user's experience impacted by other participants.
* Consider providing host-controls so that playback isn't overly chaotic. If host controls are provided, make sure everyone can participate in some way, even if it's just recommending content to the host.
* If content is not available for everyone in the activity (for example, geo-restricted content), make sure that information is known when the content is queued and when it's playing. The host is generally interested in creating a good experience for their friends. Playback of content should be in sync with others in a local channel or call.
* Familiar design patterns can help to get people to participate in your Activity right away. Most Discord users are extremely familiar with media browsing (Spotify, YouTube, Netflix, etc.) and will immediately understand your content if you follow these patterns.
* Mobile device volume can be controlled by a device's built-in controls (volume buttons on the side of the device) and work as expected across desktop, mobile, and browsers.
***
## Technical Considerations
### Developing for the iframe
* Remember that you need to contend with the Discord Client itself for resources (CPU, RAM and GPU) as that client is still fully running while executing your Activity.
* Prioritize time-to-first-interaction.
* Minimize the amount of Web Assembly (WASM) as much as possible in your Activity.
* Older iOS devices are especially affected by WebKit's optimization compilation pass of WASM that occurs during the first few minutes of usage of an Activity. To varying degrees, this will cause noticeable stutters, device thermal issues, and possibly degraded Discord's AV quality during those early minutes.
* All network traffic is routed through the Discord Proxy for various security reasons.
* Create multiple versions of your Activity in the Developer Portal for less and more stable development versioning (e.g. Development, Staging, Production). This allows you to show and test the last stable build while also having development and staging environments.
### Developing in Unity
Developing for the iframe in Unity is possible, but will likely require extensive min/maxing of all facets of your game to meet performance and quality standards. Other, web-first, game engines are generally more performant out of the box.
***
## Quality and Testing
### Verify User Interface and User Experience
* Ensure all buttons and UI elements function correctly without any blocking issues for standard Activity use or gameplay.
* Test Discord usernames handling, ensuring they display appropriately based on the chosen format (Discord username or server identity).
### Test for Performance
* Evaluate performance to ensure it is satisfactory for the majority of users across phones, tablets, and desktop machines and across multiple operating systems.
* Lag or slowness should not significantly impact the Activity or gameplay experience.
### Test for Audio
* Check that sound effects and background music work as intended, with volume settings functioning correctly.
* Being able to hear when playing on a voice call is important.
### Validate Activity Controls
* Confirm that all game controls, including clicks, drag, key controls, swiping, and hovers, work as expected and are suitable for the platform.
* Test in-app tutorials and instructions for accuracy and relevance to the current activity functionality and user platform (desktop or mobile).
### Mobile Device UI Considerations
* Watch out for potential issues on devices with top notches or cameras in specific corners that may hide or cut off UI elements and buttons, rendering them untappable.
* Be mindful of Android curved screen edges that could obscure or make elements and buttons inaccessible.
* Take into account iOS/Android swipe gestures that may interfere with UI elements or buttons located at the bottom of the screen.
* Ensure that Android devices with a back button do not overlap with elements or buttons at the bottom of the screen, preventing users from tapping them.
* Respect the safe area defined by the platform to prevent any buttons or content from being cut off or non-functional.
### Desktop Accessibility Guidelines
* Enable users to cycle through Discord buttons and fields on desktop by pressing TAB repeatedly, ensuring that the activity does not capture control.
* Implement the functionality for the ESC key to close any open in-activity modals, providing a consistent user experience.
* Opt for color-blind friendly colors with high contrast, especially for crucial game elements that need to be distinguishable based on color.
* Avoid using similar colors for objects that are essential for gameplay but differ only in color, as this may pose challenges for color-blind users.
# Activity Development Guides Index
Source: https://docs.discord.com/developers/activities/development-guides
Development guides for building Discord Activities with the Embedded App SDK.
These guides include suggested development practices, SDK commands, and user flows for you to consider while building your Activity. These will help to provide your users with a consistent and clear experience while interacting with your application.
## Local Development
Get up and running with a local development application.
How to launch your app on mobile and desktop Discord clients.
Configure the Discord proxy to allow network requests to necessary external endpoints.
How to use the various levels of logging while building your application.
## User Actions
Open an external link from within your app.
Open the Application Channel Invite dialog within Discord.
Open a dialog to share media from your application to a channel, DM, or GDM.
Configure a command that allows users to open your Activity from the App Launcher.
Open a dialog to enable hardware acceleration for compute-intensive applications.
## Mobile
Update your application settings to support iOS and Android.
Ensure that your app's assets fall within mobile-safe areas.
Respond to thermal state changes surfaced from iOS and Android.
## Layout
Configure and subscribe to changes in application orientation.
Subscribe to layout mode changes to update your application's user interface.
## Networking
Working with our Activity Proxy
Generate a full URL when working with network requests.
Allow network requests to external resources from inside the Discord proxy.
Keep things safe and secure in your Activity.
## Multiplayer Experience
Managing instances to ensure users join the same instance as their friends.
Use the SDK to fetch the users currently connected to an instance.
Retrieve and render the usernames and avatars of users connected to your application.
Validating activity sessions are via a Discord client before adding them to an instance's session.
## Growth and Referrals
Encourage your users to share links to your activity by adding tracking and offering rewards for engagement.
For off-platform sharing of rewards, promotions, or limited time experiences.
An API to be able to generate ephemeral links with a customizable embed.
## Assets & Metadata
Best practices for configuring how your application shows up in Discord.
Best practices for configuring how your application shows up in Discord.
## Rich Presence
Display live activity data on user profiles while they play your Activity.
Design tips and a launch checklist for a polished Rich Presence integration.
## Production Readiness
Manage asset caching in your application and the Discord Activity proxy.
Stay within rate limits to keep the fun going in your Activity.
Network routing considerations when preparing your Activity for production use.
Future-proof your application and support new commands as they become available in the SDK.
# Assets and Metadata
Source: https://docs.discord.com/developers/activities/development-guides/assets-and-metadata
Guide to managing Activity assets, metadata, and configuration.
## Setting Up Activity Metadata
The Activity Shelf is where users can see what Activities can be played. It has various metadata and art assets that can be configured.
To update your app's metadata in the Discord Developer Portal, navigate to the `Settings -> General Information` tab of your app.
* **Application Name:** The publicly visible name of your app.
* **Application Icon:** The publicly visible icon for your app.
* **Application Description:** The application description is shown in the view of the Activity Shelf Item.
* **Max Participants:** The max participants indicate the maximum number of players for your application.
* Max Participants is displayed above the name in the 1-up view: `Up to X participants`.
* Leaving this field empty defaults to `Unlimited participants`.
* Max Participants is also displayed under the name in the 2-up view.
An app can have a different application name and avatar from the application's bot username and avatar. Both sets of metadata are public-facing and may be visible in various situations when a user interacts with your app. You can view your bot's username on the `Settings -> Bot` tab.
***
## Setting Up Activity Art Assets
The Activity Shelf is where users can see what Activities can be played. It has various metadata and art assets that can be configured.
To update your app's embedded-specific art assets in the Discord Developer Portal, navigate to the [Activities -> Art Assets](https://discord.com/developers/applications/select/embedded/assets) tab of your app.
## Embedded Background
Used as a background overlay in Grid view. Artwork should be clustered around the edges of the image leaving space in the center of the image so the UI does not clash with it.
#### Specifications
* 16:9 aspect ratio
* At least 1024 pixels wide
## Cover Art
Used as the main image in the Activity Shelf. It is suggested that this image contain the title and some art in the background.
#### Specifications:
* Image can be displayed at both 16:9 and 13:11 aspect ratios
* At least 1024 pixels wide
## App Tile
There are two views of an application tile. The regular size tile (2-up tile) and the larger "featured" application tile (1-up tile).
## Video Preview
Hovering over the cover image should start playing a preview video of the Application. The preview videos should be no more than 10 seconds long. If no video is provided, nothing will happen as you hover over the application.
#### Specifications: 640 x 360, mp4 format, under 10 seconds long, under 1 MB in size
# Growth and Referrals
Source: https://docs.discord.com/developers/activities/development-guides/growth-and-referrals
Implement growth strategies and referral systems in Discord Activities.
## Prompting Users to Share Incentivized Links
Incentivized sharing can help grow your Activity through network effects. You can use links in several different ways such as:
* **Referral links.** Users can copy referral links inside your Activity, which include their Discord user ID (`https://discord.com/activities/?referrer_id=123456789`), and they can send to their friends. If their friend accepts and starts playing your game, then you gift the referrer something inside your game.
* **Promotions.** You can run a temporary promotion on social media, where you offer a reward if they start playing now. Share a custom link on your social media (`https://discord.com/activities/?custom_id=social012025` ). Anyone who clicks that specific link receives something inside your game.
* **Social deep-links.** Currently, when users launch an Activity, they all land in the same place. Instead, you can start deep-linking to contextually relevant points in your game. For example, user A can copy a link inside your Activity for engaging other users (`https://discord.com/activities/?referrer_id=123456789&custom_id=visit-location`), and sends the link to their friends in a DM or channel. Then, user B who clicks the link gets taken directly to user A’s location.
* **Turn-based deep-links.** When you send an “it’s your turn” DM to a user, you can include a link which takes them directly to the right game instance and the turn they need to take.
* **Affiliate marketing.** You can work with affiliates (influencers, companies, etc) to advertise your game to their followings, and reward them via a custom link (`https://discord.com/activities/?custom_id=influencer1`). Then, for every user that starts playing because of said influencer, you can then pay out to the influencer.
* **Source attribution.** You can use the `custom_id` parameter to figure out how much traffic you’re getting from different marketing sources.
This guide covers implementing a referral link which will feature a reward system for users who share links and those who click them.
#### Implementation Overview
1. Create and track an incentivized link for a promotional campaign, then prompt users to share the link
2. Handle incoming referrals and grant valid rewards
#### Sharing Links
When implementing sharing, you'll need to:
1. Generate a unique ID for tracking the promotion
2. Call the [`shareLink`](/developers/developer-tools/embedded-app-sdk#sharelink) command
3. Track the share attempt
```javascript theme={"system"}
// Generate a unique ID for this promotion
// This could be per-campaign, per-user, or per-share depending on your needs
const customId = await createPromotionalCustomId();
try {
const { success } = await discordSdk.commands.shareLink({
message: 'Click this link to redeem 5 free coins!',
custom_id: customId,
});
if (success) {
// Track successful share for analytics/limiting
await trackSuccessfulShare(customId);
}
} catch (error) {
// Handle share failures appropriately
console.error('Failed to share link:', error);
}
```
#### Handling Incoming Referrals
When a user clicks a shared link, your activity will launch with referral data available through the SDK:
```javascript theme={"system"}
// Early in your activity's initialization
async function handleReferral() {
// Validate the referral data
if (!discordSdk.customId || !discordSdk.referrerId) {
return;
}
try {
// Verify this is a valid promotion and hasn't expired
const promotion = await validatePromotion(discordSdk.customId);
if (!promotion) {
console.log('Invalid or expired promotion');
return;
}
// Prevent self-referrals
if (discordSdk.referrerId === currentUserId) {
console.log('Self-referrals not allowed');
return;
}
// Grant rewards to both users
await grantRewards({
promotionId: discordSdk.customId,
referrerId: discordSdk.referrerId,
newUserId: currentUserId
});
} catch (error) {
console.error('Failed to process referral:', error);
}
}
```
#### Link Sharing Best Practices
* Generate unique, non-guessable `customId`s
* Track and validate referrals to prevent abuse
* Handle edge cases like expired promotions gracefully
* Consider implementing cool-down periods between shares
* Do not override the `referrer_id` query parameter directly. When present, `referrer_id` is expected to be a Discord snowflake-type user ID, otherwise it will be set to the message's author id.
***
## Creating and Managing Custom Incentivized Links
This guide covers creating a customizable [Incentivized Link](/developers/activities/development-guides/growth-and-referrals#prompting-users-to-share-incentivized-links) through the dev portal, and then retrieving the link to be able to share it off-platform. Incentivized Links are used to customize how the embed appears to users.
#### Creating a Link
1. In your Application's portal, visit the [Custom Links page](https://discord.com/developers/applications/select/embedded/custom-links) under the Activities heading in the navigation pane.
2. On the Custom Links page, click `Create New` to create a new link.
3. You will need to upload an image with an aspect ratio of 43:24.
4. Title, and description are also required.
5. `custom_id` is an optional field, an explicit `custom_id` query parameter on the link itself will always override the set `custom_id`.
6. Click Save.
#### Editing a Link
1. Click on a row to open up the modal with all of the data loaded in ready for your edits.
2. Change the description to something else.
3. Click Update.
#### Copying a Link
Once you're satisfied with your changes you can click on the copy icon on the row, it'll change colors to green indicating that it copied to your clipboard. You are now able to share this link anywhere. The link will look like: `https://discord.com/activities/?link_id=0-123456789`. Even if you've set a `custom_id`, it won't be explicitly included in the link but will be loaded once a user clicks on the link. You can then further shorten this URL if you'd like.
#### Deleting a Link
1. Click on the trash icon on the row of the link you're trying to delete.
2. You'll have a confirm dialog pop up.
Deleting is irreversible and immediate. Ensure that your link isn't in active use before deleting and/or that your activity gracefully handles any click-throughs from the link.
#### Best Practices
* Generate unique, non-guessable `customId`s
* Track and validate referrals to prevent abuse
* Gracefully handle expirations in your activity for any custom links that are limited time but still live off-platform.
#### User Experience
Users will see an embed with your information displayed. Clicking "Play" opens the activity and passes through the `custom_id` you've set. A `referrer_id` will be present for links shared on Discord.
***
## Generating a Custom Link Within Your Activity
This guide covers creating a customizable [Incentivized Link](/developers/activities/development-guides/growth-and-referrals#prompting-users-to-share-incentivized-links) within your activity, and using the `shareLink` API to share the link.
* Allows you to customize the way the link is presented to users via the embed
* Can be generated on-demand within your activity
* Ephemeral, 30 day TTL
* Does not show up in the developer portal
#### Generating a Link
```javascript theme={"system"}
// Convert an image array buffer to base64 string
const image = base64EncodedImage;
// Generate the quick activity link
const linkIdResponse = await fetch(`${env.discordAPI}/applications/${env.applicationId}/quick-links/`, {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: {
custom_id: 'user_123/game_456',
description: 'I just beat level 10 with a perfect score',
title: 'Check out my high score!',
image,
}
});
const {link_id} = await linkIdResponse.json();
// Open the Share modal with the generated link
const {success} = await discordSdk.commands.shareLink({
message: 'Check out my high score!',
link_id,
});
success ? console.log('User shared link!') : console.log('User did not share link!');
```
# Layout
Source: https://docs.discord.com/developers/activities/development-guides/layout
Design effective layouts and user interfaces for Discord Activities.
## Application Orientation
#### Locking Application Orientation
This SDK provides APIs for locking the application to specific orientations. The possible lock states are `UNLOCKED`, `PORTRAIT`, and `LANDSCAPE`. `lock_state` is the default lock state, and it affects the app orientation when the application is focused. `picture_in_picture_lock_state` determines the PIP aspect ratio, and `grid_lock_state` determines the grid tile aspect ratio for the application. When `picture_in_picture_lock_state` is not set, the application PIP falls back to `lock_state` to determine the aspect ratio. When `grid_lock_state` is not set, the application grid tile falls back to `picture_in_picture_lock_state` to determine its aspect ratio, and if `picture_in_picture_lock_state`is not set, it uses `lock_state`.
Calling `setOrientationLockState` with an `undefined` or omitted value for `picture_in_picture_lock_state` or `grid_lock_state` will not change the corresponding lock states for the application. Calling `setOrientationLockState` with a null value for `picture_in_picture_lock_state` or `grid_lock_state` will clear the application's corresponding lock states such that those layout modes will use the fallback lock states.
```javascript theme={"system"}
import {DiscordSDK, Common} from '@discord/embedded-app-sdk';
const discordSdk = new DiscordSDK(clientId);
await discordSdk.ready();
// Set a default lock state
discordSdk.commands.setOrientationLockState({lock_state: Common.OrientationLockStateTypeObject.LANDSCAPE});
// or set both a default lock state and a picture-in-picture lock state
discordSdk.commands.setOrientationLockState({
lock_state: Common.OrientationLockStateTypeObject.PORTRAIT,
picture_in_picture_lock_state: Common.OrientationLockStateTypeObject.LANDSCAPE,
grid_lock_state: Common.OrientationLockStateTypeObject.LANDSCAPE,
});
```
#### Configuring Default Orientation Lock State Through the Developer Portal
It's also possible to configure an application with a default orientation lock state via the Developer Portal. Using this method, the Discord app will apply the orientation lock when launching the application before the SDK has been initialized. This can create a smoother application launch flow where the application starts in the correct orientation rather than switching to the correct orientation after some delay after the application requests an orientation lock via the SDK. The Developer Portal supports setting a different default orientation lock states for phones versus tablets.
#### Subscribing to Screen Orientation Updates
To listen to the screen orientation (which is sometimes different from the physical device orientation), subscribe to the `ORIENTATION_UPDATE` event. Discord will publish the current orientation upon event subscription, and it'll also publish any orientation changes that happen afterward.
```javascript theme={"system"}
const handleOrientationUpdate = (update: {screen_orientation: number}) => {
switch (update.screen_orientation) {
case Common.OrientationTypeObject.PORTRAIT:
...
case Common.OrientationTypeObject.LANDSCAPE:
...
default:
...
}
}
discordSdk.subscribe('ORIENTATION_UPDATE', handleOrientationUpdate);
```
***
## Application Layout Mode
There are three layout modes that an application can be in: focused, picture-in-picture (PIP), or grid mode. Activities can subscribe to the layout mode to determine when to optionally change their layouts to optimize for each layout mode. Old Discord clients only support the `ACTIVITY_PIP_MODE_UPDATE` event, while new Discord clients support both `ACTIVITY_PIP_MODE_UPDATE` and `ACTIVITY_LAYOUT_MODE_UPDATE`. Use `subscribeToLayoutModeUpdatesCompat` and `unsubscribeFromLayoutModeUpdatesCompat` to subscribe to both events with backward compatibility for old Discord clients that only support `ACTIVITY_PIP_MODE_UPDATE`. Here's an example using React:
```javascript theme={"system"}
export default function LayoutMode() {
const handleLayoutModeUpdate = React.useCallback((update: {layout_mode: number}) => {
...
}, []);
React.useEffect(() => {
discordSdk.subscribeToLayoutModeUpdatesCompat(handleLayoutModeUpdate);
return () => {
discordSdk.unsubscribeFromLayoutModeUpdatesCompat(handleLayoutModeUpdate);
};
}, [handleLayoutModeUpdate]);
}
```
# Local Development
Source: https://docs.discord.com/developers/activities/development-guides/local-development
Set up local development environment for Discord Activities.
## Run Your Application Locally
It is possible to load your application via a localhost port or other unique URL. This URL must support an HTTPS connection to load on the web/desktop Discord app (HTTPS is not required for mobile). The downside to this flow is that your application's network traffic will not pass through Discord's proxy, which means any requests made by the application will need to use a full URL instead of a ["mapped"](/developers/activities/development-guides/local-development#url-mapping) URL.
To run your locally hosted application, follow the instructions for [Launching your Application from the Discord Client](/developers/activities/development-guides/local-development#launch-your-application-from-the-discord-client) and set the Application URL Override to the address of your application's web server.
### Running Your Application Through A Network Tunnel
Although it is possible to test your application locally, we recommend developing and testing against the Discord proxy. This is helpful to make sure all URLs behave as expected before your application runs in production. One technique to enable testing locally against the proxy is to use a network tunneling tool, such as [cloudflared](https://github.com/cloudflare/cloudflared#installing-cloudflared). A typical pattern is for each developer to have their own "development-only" application. To set up a local environment to run through Discord's proxy, you will need to do the following:
1. Create a new application in the Discord Developer portal.
2. Enable Activities for your app.
3. Set up the application's [URL mapping](/developers/activities/development-guides/local-development#url-mapping).
4. Locally, spin up your web server.
5. Install and run a tunnel solution, such as [cloudflared](https://github.com/cloudflare/cloudflared#installing-cloudflared). You will point it to your local web server.
Your web server can be HTTP and your network tunnel can upgrade the connection to HTTPS.
If using cloudflared, you will run the following command, replace `3000` with your web server's port.
```
cloudflared tunnel --url http://localhost:3000
```
Once you run this command, you will receive your publicly accessible network tunnel address from cloudflared.
```
Your quick Tunnel has been created! Visit it at (it may take some time to be reachable):
https://funky-jogging-bunny.trycloudflare.com
```
In the Discord Developer Portal, update the Application URL mapping for `/` url to `funky-jogging-bunny.trycloudflare.com` to match your network tunnel address and save your changes.
If you do not own the URL that you are using to host the application (i.e. ngrok's free tier), someone else could claim that domain and host a malicious site in its place. Please be aware of these risks, and if you have to use a domain you do not own, be sure to reset your URL mapping when you are done using the tunnel.
Follow the instructions for [Launching your Application from the Discord Client](/developers/activities/development-guides/local-development#launch-your-application-from-the-discord-client). Application URL Override should not be enabled.
### Running Your Application In Production
The flow for setting up your production application is very similar:
1. If not made yet, create a new application.
2. Enable Activities for your app.
3. Set up the application's [URL Mapping](/developers/activities/development-guides/local-development#url-mapping). The URL for your application's html should be set to the `/` route.
4. Follow the instructions for [Launching your Application from the Discord Client](/developers/activities/development-guides/local-development#launch-your-application-from-the-discord-client). Application URL Override should not be enabled.
This application now uses the same configuration it will use once it is fully published ✨.
***
### Launch your application from the Discord Client
You will be able to see and launch all activities owned by you or any teams you are a member of via the Developer Activity Shelf. One caveat is that the activity will not be shown on the current platform (web/ios/android) unless you have checked your platform in [Settings/Supported Platforms](https://discord.com/developers/applications/select/embedded/settings) on the developer portal.
To see you app inside of Discord in the Activity Shelf:
#### Web
1. Select ⚙️ User Settings > App Settings > Advanced and toggle on `Developer Mode`
2. Close the settings window and enter a voice channel.
3. From either the RTC Panel or the Center Control Tray, click on the "Rocket Button" to open the Activity shelf. You should now see all of the same applications that you have access to in the developer portal. Note: The shelf will only include applications which have been flagged as "Embedded".
4. Click on an activity to launch it!
#### Mobile
1. From your User Profile, select Appearance, and then toggle "On" `Developer Mode`
2. Enter a voice channel
3. Click on an activity to launch it!
***
### URL Mapping
Activities in Discord are "sandboxed" via a Discord proxy. This is done to hide the users' IP addresses, your application's IP addresses, and to block URLs from known malicious endpoints. As an application owner, you can configure the proxy to allow network requests to external endpoints.
Because your application is "sandboxed", it will be unable to make network requests to external URLs. Let's say you want request `https://some-api.com`. To enable reaching this url from inside your application, you will create a new url mapping, with the `PREFIX` set to
`/api` and `TARGET` set to `some-api.com`. Now you can make requests to `/api` from inside of your application, which will be forwarded, via Discord's proxy to `some-api.com`.
#### How to set a URL Mapping
To add or modify your application's URL mappings, click on [Activities -> URL Mappings](https://discord.com/developers/applications/select/embedded/url-mappings) and set the prefix and target values for each mapping as needed.
#### Prefix/Target formatting rules
* URL mappings can utilize any url protocol, (https, wss, ftp, etc...), which is why the URL target should not include a protocol. For example, for a URL target, do not put `https://your-url.com`, instead, omit `https://` and use `your-url.com`.
* Parameter matching can be used to help map external domain urls. For example, if an external url has many subdomains, such as `foo.google.com`, `bar.google.com`, then you could use the following mapping:
| PREFIX | TARGET |
| --------------------- | ------------------------ |
| `/google/{subdomain}` | `{subdomain}.google.com` |
* Targets must point to a directory; setting a target to a file (e.g. `example.com/index.html`) is unsupported and may lead to unexpected behavior.
* Because of how URL globbing works, if you have multiple prefix urls with the same initial path, you must place the shortest of the prefix paths last in order for each url mapping to be reachable. For example, if you have `/foo` and `/foo/bar`, you must place the url `/foo/bar` before `/foo` or else the mapping for `/foo/bar` will never be reached.
| ✅ DO | ❌ DON'T |
| -------------------------------- | ---------------------------------------------------------- |
| Requests mapped correctly | Requests to /foo/bar will incorrectly be sent to `foo.com` |
|
|
|
#### Exceptions
The aforementioned "sandbox" is enforced by a [Content Security Policy (CSP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP). We have some notable exceptions to our CSP, meaning application clients may make requests to these URLs without hitting the proxy and therefore without establishing mappings. Notable exceptions include:
* `https://discord.com/api/`
* `https://canary.discord.com/api/`
* `https://ptb.discord.com/api/`
* `https://cdn.discordapp.com/attachments/`
* `https://cdn.discordapp.com/avatars/`
* `https://cdn.discordapp.com/icons/`
* `https://media.discordapp.net/attachments/`
* `https://media.discordapp.net/avatars/`
* `https://media.discordapp.net/icons/`
***
### Logging
By default, the SDK will send any console `log`, `warn`, `error`, `info`, and `debug` events triggered by your app to the Discord application.
#### Viewing Logs on Desktop
Desktop logs are viewable through the console tab inside a browser's Developer Tools. See the [Troubleshooting Console Log Errors](https://support.discord.com/hc/en-us/articles/115001239472-Troubleshooting-Console-Log-Errors) support article for more information.
The Public Test Build (PTB) Discord client also allows inspecting your logs from the `View -> Developer -> Toggle Developer Tools` menu. It can be downloaded at [https://discord.com/downloads](https://discord.com/downloads).
#### Viewing Logs on Mobile
Mobile logs are viewable via the `Debug Logs` option inside User Settings on the mobile App. It is only discoverable when you have `Developer Mode` enabled.
1. On the bottom navigation, tap on your avatar and then the gear icon to open your `User Settings`.
2. Tap `Appearance`.
3. Slide the `Developer Mode` toggle to ON.
4. The `Debug Logs` option will be available under the `DEV ONLY` section.
#### Filtering for Application Logs
Inside the Debug Logs view, you can search for your own application logs with the possible keywords:
* `RpcApplicationLogger`
* Your Application ID
Each log line is formatted as: `[RpcApplicationLogger] - message`
The first section of Debug Logs are not your application logs but Discord specific app startup info which is not relevant to your application.
When you scroll down the page, your application logs should be visible.
#### Sharing Application Logs from Mobile
With `Developer Mode` enabled, you can share your application logs from within a Voice Channel.
1. In the voice channel, swipe from the bottom to see the expanded voice controls. Tap on `Share Application Logs`.
2. You'll be presented with a native share sheet where you can save the logs to a file or share it as a message.
#### Disabling Logging
If you do not want logs to be forwarded to the browser, you can disable it with the optional configuration object.
```javascript theme={"system"}
import {DiscordSDK} from '@discord/embedded-app-sdk';
const discordSdk = new DiscordSDK(clientId, {
disableConsoleLogOverride: true,
});
```
#### Forwarding Log Messages
You can forward specific log messages via the SDK command `captureLog` as shown below.
```javascript theme={"system"}
import {DiscordSDK} from '@discord/embedded-app-sdk';
const discordSdk = new DiscordSDK(clientId);
await discordSdk.ready();
discordSdk.commands.captureLog({
level: 'log',
message: 'This is my log message!',
});
```
# Mobile
Source: https://docs.discord.com/developers/activities/development-guides/mobile
Design and optimize Discord Activities for mobile devices.
## Supported Platforms: Web, iOS, Android
By default, your Activity will be launchable on web/desktop. To enable or disable support for Web/iOS/Android, do the following:
* Visit the [developer portal](https://discord.com/developers/applications)
* Select your application
* Select [Activities -> Settings](https://discord.com/developers/applications/select/embedded/settings) in the left-side of the developer portal
* From check the appropriate checkboxes in the developer portal, and save your changes
***
## Mobile Safe Areas
As an example, you can define your safe area insets as below in CSS:
```css theme={"system"}
:root {
--sait: var(--discord-safe-area-inset-top, env(safe-area-inset-top));
--saib: var(--discord-safe-area-inset-bottom, env(safe-area-inset-bottom));
--sail: var(--discord-safe-area-inset-left, env(safe-area-inset-left));
--sair: var(--discord-safe-area-inset-right, env(safe-area-inset-right));
}
```
This prefers the `--discord-safe-area-inset-*` variable and will fallback to the env values for iOS + any local dev testing that is done outside of Discord.
You can then reference these values:
```css theme={"system"}
body {
padding-left: var(--sail);
padding-right: var(--sair);
padding-top: var(--sait);
padding-bottom: var(--saib);
}
```
***
## Mobile Thermal States
You may need to respond to thermal state changes using recommendations from [thermal states surfaced by mobile devices](https://developer.apple.com/library/archive/documentation/Performance/Conceptual/power_efficiency_guidelines_osx/RespondToThermalStateChanges.html) to improve the user experience.
Discord's Embedded App SDK provides an abstraction over [Apple's thermal state APIs](https://developer.apple.com/library/archive/documentation/Performance/Conceptual/power_efficiency_guidelines_osx/RespondToThermalStateChanges.html) and [Android's thermal state APIs](https://source.android.com/docs/core/power/thermal-mitigation#thermal-api).
Here's how Discord's abstraction maps to Apple's thermal states and Android's thermal states.
```javascript theme={"system"}
enum ThermalState {
NOMINAL = 0, // maps to "nominal" on iOS and "none" on Android
FAIR = 1, // maps to "fair" on iOS and "light" / "moderate" on Android
SERIOUS = 2, // maps to "serious" on iOS and "severe" on Android
CRITICAL = 3, // maps to "critical" on iOS and "critical" / "emergency" / "shutdown" on Android
}
```
The Embedded App SDK allows developers to subscribe to these thermal state changes.
```javascript theme={"system"}
const handleThermalStateUpdate = (update: {thermal_state: number}) => {
switch (thermalState) {
case Common.ThermalStateTypeObject.NOMINAL:
...
case Common.ThermalStateTypeObject.FAIR:
...
case Common.ThermalStateTypeObject.SERIOUS:
...
case Common.ThermalStateTypeObject.CRITICAL:
...
default:
...
}
}
discordSdk.subscribe('THERMAL_STATE_UPDATE', handleThermalStateUpdate);
```
Discord will publish the current thermal state upon event subscription, and it will also publish any thermal state changes that happen afterward.
On Android devices, the thermal state updates will only be available on Android 10 and higher.
# Multiplayer Experience
Source: https://docs.discord.com/developers/activities/development-guides/multiplayer-experience
Design and implement engaging multiplayer experiences in Discord Activities.
## Activity Instance Management
When a user clicks "Join Application", they expect to enter the same application that their friends are participating in. Whether the application is a shared drawing canvas, board game, collaborative playlist, or first-person shooter; the two users should have access to the same shared data. In this documentation, we refer to this shared data as an **application instance**.
The Embedded App SDK allows your app to talk bidirectionally with the Discord Client. The `instanceId` is necessary for your application, as well as Discord, to understand which unique instance of an application it is talking to.
#### Using instanceId
The `instanceId` attribute is available as soon as the SDK is constructed, and does not require the SDK to receive a `ready` payload from the Discord client.
```javascript theme={"system"}
import {DiscordSDK} from '@discord/embedded-app-sdk';
const discordSdk = new DiscordSDK(clientId);
// available immediately
const instanceId = discordSdk.instanceId;
```
The `instanceId` should be used as a key to save and load the shared data relevant to an application. This ensures that two users who are in the same application instance have access to the same shared data.
##### Semantics of instanceId
Instance IDs are generated when a user launches an application. Any users joining the same application will receive the same `instanceId`. When all the users of an application in a channel leave or close the application, that instance has finished its lifecycle, and will not be used again. The next time a user opens the application in that channel, a new `instanceId` will be generated.
***
## Instance Participants
Instance Participants are any Discord user actively connected to the same Application Instance. This data can be fetched or subscribed to.
```javascript theme={"system"}
import {DiscordSDK, Events, type Types} from '@discord/embedded-app-sdk';
const discordSdk = new DiscordSDK('...');
await discordSdk.ready();
// Fetch
const participants = await discordSdk.commands.getInstanceConnectedParticipants();
// Subscribe
function updateParticipants(participants: Types.GetActivityInstanceConnectedParticipantsResponse) {
// Do something really cool
}
discordSdk.subscribe(Events.ACTIVITY_INSTANCE_PARTICIPANTS_UPDATE, updateParticipants);
// Unsubscribe
discordSdk.unsubscribe(Events.ACTIVITY_INSTANCE_PARTICIPANTS_UPDATE, updateParticipants);
```
***
## Render Avatars and Names
Check out detailed documentation on where and how Discord stores common image assets [here](/developers/reference#image-formatting-cdn-endpoints).
Here's a basic example for retrieving a user's avatar and username
```javascript theme={"system"}
// We'll be referencing the user object returned from authenticate
const {user} = await discordSdk.commands.authenticate({
access_token: accessToken,
});
let avatarSrc = '';
if (user.avatar) {
avatarSrc = `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png?size=256`;
} else {
const defaultAvatarIndex = (BigInt(user.id) >> 22n) % 6n;
avatarSrc = `https://cdn.discordapp.com/embed/avatars/${defaultAvatarIndex}.png`
}
const username = user.global_name ?? `${user.username}#${user.discriminator}`;
// Then in your HTML/JSX/etc...
{username}
```
#### Rendering guild-specific avatars and nicknames
In order to retrieve a user's guild-specific avatar and nickname, your application must request the `guilds.members.read` scope. Note, this only grants the information for that instance of the application's user. To display the guild-specific avatar/nickname for all application users, any info retrieved from `guilds.members.read` scope'd API calls must be shared via your application's server.
Here's an example of how to retrieve the user's guild-specific avatar and nickname:
```javascript theme={"system"}
// We'll be referencing the user object returned from authenticate
const {user} = await discordSdk.commands.authenticate({
access_token: accessToken,
});
// When using the proxy, you may instead replace `https://discord.com` with `/discord`
// or whatever url mapping you have chosen via the developer portal
fetch(`https://discord.com/api/users/@me/guilds/${discordSdk.guildId}/member`, {
method: 'GET',
headers: {
Authorization: `Bearer ${accessToken}`,
},
})
.then((response) => {
return response.json();
})
.then((guildsMembersRead) => {
let guildAvatarSrc = '';
// Retrieve the guild-specific avatar, and fallback to the user's avatar
if (guildsMembersRead?.avatar) {
guildAvatarSrc = `https://cdn.discordapp.com/guilds/${discordSdk.guildId}/users/${user.id}/avatars/${guildsMembersRead.avatar}.png?size=256`;
} else if (user.avatar) {
guildAvatarSrc = `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png?size=256`;
} else {
const defaultAvatarIndex = (BigInt(user.id) >> 22n) % 6n;
avatarSrc = `https://cdn.discordapp.com/embed/avatars/${defaultAvatarIndex}.png`;
}
// Retrieve the guild-specific nickname, and fallback to the username#discriminator
const guildNickname = guildsMembersRead?.nick ?? (user.global_name ?? `${user.username}#${user.discriminator}`);
});
```
This example is being done entirely on the client, however, a more common pattern is to instead, do the following:
* Store the user's access token on the application server
* Retrieve the user's guild-specific avatar and nickname via the application's server
* Serve all of the application's avatar/nicknames via the application's server
***
## Preventing unwanted activity sessions
Activities are surfaced through iframes in the Discord app. The activity website itself is publicly reachable at `.discordsays.com`. Activities will expect to be able to communicate with Discord's web or mobile client via the Discord SDK's RPC protocol. If a user loads the activity's website in a normal browser, the Discord RPC server will not be present, and the activity will likely fail in some way.
It is theoretically possible for a malicious client to mock Discord's RPC protocol or load one activity website when launching another. Because the activity is loaded inside Discord, the RPC protocol is active, and the activity is none the wiser.
### Using the Activity Instance API
To enable an activity to "lock down" activity access, we encourage utilizing the `get_activity_instance` API, found at `discord.com/api/applications//activity-instances/'`. The route requires a Bot token of the application. It returns a serialized active activity instance for the given application, if found, otherwise it returns a 404. Here are two example responses:
```javascript theme={"system"}
curl https://discord.com/api/applications/1215413995645968394/activity-instances/i-1234567890-gc-912952092627435520-912954213460484116 -H 'Authorization: Bot '
{"message": "404: Not Found", "code": 0}
curl https://discord.com/api/applications/1215413995645968394/activity-instances/i-1276580072400224306-gc-912952092627435520-912954213460484116 -H 'Authorization: Bot '
{"application_id":"1215413995645968394","instance_id":"i-1276580072400224306-gc-912952092627435520-912954213460484116","launch_id":"1276580072400224306","location":{"id":"gc-912952092627435520-912954213460484116","kind":"gc","channel_id":"912954213460484116","guild_id":"912952092627435520"},"users":["205519959982473217"]}
```
With this API, the activity's backend can verify that a client is in fact in an instance of that activity before allowing the client to participate in any meaningful gameplay. How an activity implements "session verification" is left to the developer's discretion. The solution can be as granular as gating specific features or as binary as not returning the activity HTML except for valid sessions.
###### Validating Proxy Request Headers
For apps that want additional security validation, Discord provides an optional proxy authentication system. When your embedded app makes requests through Discord's proxy, each request can include cryptographic headers that prove the request's authenticity.
Each proxy-authenticated request is sent with the following headers:
* `X-Signature-Ed25519` as a cryptographic signature
* `X-Signature-Timestamp` as a Unix timestamp
* `X-Discord-Proxy-Payload` as a base64-encoded payload containing user context
If you choose to use proxy authentication, you can validate these headers to ensure requests are legitimate. If the signature fails validation, your app should respond with a `401` error code.
Below are some code examples that show how to validate the headers sent in proxy-authenticated requests.
**JavaScript**
```js theme={"system"}
const nacl = require("tweetnacl");
// Your public key can be found on your application in the Developer Portal
const PUBLIC_KEY = "APPLICATION_PUBLIC_KEY";
const signature = req.get("X-Signature-Ed25519");
const timestamp = req.get("X-Signature-Timestamp");
const payload = req.get("X-Discord-Proxy-Payload");
// Decode the base64 payload
const payloadBytes = Buffer.from(payload, "base64");
const payloadString = payloadBytes.toString("utf-8");
const payloadData = JSON.parse(payloadString);
// Verify timestamp matches payload
if (payloadData.created_at.toString() !== timestamp) {
return res.status(401).end("invalid request timestamp");
}
// Check if token has expired
if (payloadData.expires_at < Math.floor(Date.now() / 1000)) {
return res.status(401).end("expired proxy token");
}
// Verify the signature using tweetnacl
const isVerified = nacl.sign.detached.verify(
payloadBytes,
Buffer.from(signature, "base64"),
Buffer.from(PUBLIC_KEY, "hex")
);
if (!isVerified) {
return res.status(401).end("invalid request signature");
}
```
**Python**
```py theme={"system"}
import json
import base64
import time
from nacl.signing import VerifyKey
from nacl.exceptions import BadSignatureError
# Your public key can be found on your application in the Developer Portal
PUBLIC_KEY = 'APPLICATION_PUBLIC_KEY'
verify_key = VerifyKey(bytes.fromhex(PUBLIC_KEY))
signature = request.headers["X-Signature-Ed25519"]
timestamp = request.headers["X-Signature-Timestamp"]
payload = request.headers["X-Discord-Proxy-Payload"]
# Decode the base64 payload
payload_bytes = base64.b64decode(payload)
payload_string = payload_bytes.decode('utf-8')
payload_data = json.loads(payload_string)
# Verify timestamp matches payload
if str(payload_data['created_at']) != timestamp:
abort(401, 'invalid request timestamp')
# Check if token has expired
if payload_data['expires_at'] < int(time.time()):
abort(401, 'expired proxy token')
try:
verify_key.verify(payload_bytes, bytes.fromhex(signature))
except BadSignatureError:
abort(401, 'invalid request signature')
```
Proxy authentication is entirely optional and provided as an additional security layer for apps that choose to implement it.
In the below flow diagram, we show how the server can deliver the activity website, only for valid users in a valid activity instance:
# Networking
Source: https://docs.discord.com/developers/activities/development-guides/networking
Learn networking patterns and best practices for Discord Activities.
## Activity Proxy Considerations
All network traffic is routed through the Discord Proxy for various security reasons.
Under the hood we utilize Cloudflare Workers, which brings some restrictions, outlined below.
#### WebTransport
While we currently only support websockets, we're working with our upstream providers to enable WebTransport.
#### WebRTC
WebRTC is not supported.
***
## Construct A Full URL
There are scenarios where instead of using a relative url (`/path/to/my/thing`) you may want or need to reference the full url when making a network request. The URL is a combination of the following
1. The protocol you wish to use
2. Your application's client id
3. The discord proxy domain
4. Whatever you need to list
Here's an example of how to build a full url, using the URL constructor:
```javascript theme={"system"}
const protocol = `https`;
const clientId = '';
const proxyDomain = 'discordsays.com';
const resourcePath = '/foo/bar.jpg';
const url = new URL(`${protocol}://${clientId}.${proxyDomain}${resourcePath}`);
```
In other words, given an application client id of `12345678`
| Relative Path | Full Path |
| ------------- | -------------------------------------------------------------------------------------------- |
| /foo/bar.jpg | [https://12345678.discordsays.com/foo/bar.jpg](https://12345678.discordsays.com/foo/bar.jpg) |
***
## Using External Resources
Activities in Discord are "sandboxed" via a Discord proxy. This is done to hide the users' IP addresses as well as block URLs from known malicious endpoints. To achieve this, the Discord Developer Portal has a section for [configuring URL Mappings](/developers/activities/development-guides/local-development#url-mapping) for your application.
One edge-case of URL mappings is that third-party NPM modules or other resources may reference external (non-sandboxed) urls.
For example, if your application has an npm module that attempts to make an http request to [https://foo.library.com](https://foo.library.com), the request will fail with a `blocked:csp` error.
To get around this limitation there are several options to consider:
* Fork the library (to use mapped urls)
* Utilize a post-install utility such as [patch-package](https://www.npmjs.com/package/patch-package)
* Use our Embedded App SDK's `patchUrlMappings` API
In the above scenario, we recommend using the `patchUrlMappings` API, as it will allow a smooth transition from the non-sandboxed dev environment to the production environment.
This method call takes an array of "mappings" which will transform any external network requests to the mappings you've defined.
See the example below:
* In this example, imagine you have a third-party library which makes an HTTP request to foo.com
* In the developer portal, create a mapping like this: `/foo` -> `foo.com`
* Then in your code, when initializing the SDK, you will make a function call.
```javascript theme={"system"}
import {patchUrlMappings} from '@discord/embedded-app-sdk';
const isProd = process.env.NODE_ENV === 'production'; // Actual dev/prod env check may vary for you
async function setupApp() {
if (isProd) {
patchUrlMappings([{prefix: '/foo', target: 'foo.com'}]);
}
// start app initialization after this....
}
```
Note: `patchUrlMappings` is modifying your browser's `fetch`, `WebSocket`, and `XMLHttpRequest.prototype.open` global variables. Depending on the library, you may see side effects from using this helper function. It should be used only when necessary.
***
## Security Considerations
#### Trusting Client Data
Do not trust data coming from the Discord client as truth. It's fine to use this data in your application locally, but assume any data coming from the Discord Client could be falsified. That includes data about the current user, their nitro status, their current channel, etc. If you need this information in a trusted manner, contact Discord API directly from your application's server, with the user token you received from completing the OAuth2 flow.
Furthermore, data coming from the Discord client is not sanitized beforehand. Things like usernames and channel names are arbitrary user input. Make sure to sanitize these strings or use `.textContent` (for example) to display them safely in your UI.
#### Using Cookies
To set a cookie for your activity to use in network requests through the proxy, make sure the cookie's domain matches your app's full `{clientId}.discordsays.com` domain. You will also need to explicitly set `SameSite=None Partitioned` on the cookie. `SameSite=None` is needed as browsers refuse to store or send cookies with higher restriction levels for any navigation within an iframe. `Partitioned` then limits the use of that cookie to only Discord's iframes.
Rest assured: other activities will not be able to make requests with your activity's cookie, thanks to the Content Security Policy (CSP) limiting requests only to your own app's proxy.
# Production Readiness
Source: https://docs.discord.com/developers/activities/development-guides/production-readiness
Prepare your Discord Activity for production deployment.
## Cache Busting
All assets loaded by your application will respect [cache headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control). One exception is that Discord's application proxy will remove any cache headers for assets whose `content-type` headers include `text/html`. For all non-`text/html` content that your application plans to serve, be sure your application has a cache-busting strategy. This is often built into build processes. If your application has a static filename for its javascript or css, please be sure to implement cache busting techniques, for example [webpack enables creating dynamic file names](https://webpack.js.org/guides/caching/).
***
## Handling Rate Limits
Be sure network requests made by your application's client and server will be able to respect Discord API's rate limiting [as described here](/developers/topics/rate-limits).
See [this implementation in the Activity Starter project](https://github.com/discord/embedded-app-sdk-examples/blob/main/discord-activity-starter/packages/server/src/utils.ts) for an example of how to respect the `retry_after` header when you receive a 429 error.
***
## Static IP Addresses
If your application's server is utilizing a dynamically assigned IP address (this is standard for cloud functions), there is a non-zero chance that you will inherit from a previous bad actor an IP address which has been banned by Cloudflare. In this scenario any egress traffic from the IP address directed towards Discord's API will be banned for up-to an hour. The best way to mitigate this situation is to set up a static IP address for all of your application server's egress traffic to be routed through.
***
## Backward Compatibility
#### New Commands
When new commands become available in the embedded-app-sdk, those commands won't be supported by all Discord app versions. The new command will typically only be supported by newer Discord app versions. When an application tries to use a new command with an old Discord app version that doesn't support the command, the Discord app will respond with error code `INVALID_COMMAND` which the application can handle like this:
```javascript theme={"system"}
try {
const {permissions} = await discordSdk.commands.getChannelPermissions();
// check permissions
...
} catch (error) {
if (error.code == RPCErrorCodes.INVALID_COMMAND) {
// This is an expected error. The Discord client doesn't support this command
...
} else {
// Unexpected error
...
}
}
```
# User Actions
Source: https://docs.discord.com/developers/activities/development-guides/user-actions
Implement user interactions and actions in Discord Activities.
## Open External Link
Since Activities are sandboxed, your app will need to perform a command in order for users to launch any external links. Users will be prompted inside Discord whether or not they want to open the external link.
#### Usage
```javascript theme={"system"}
import {DiscordSDK} from '@discord/embedded-app-sdk';
const discordSdk = new DiscordSDK(clientId);
await discordSdk.ready();
// Once the sdk has established the connection with the discord client, external
// links can be launched
discordSdk.commands.openExternalLink({
url: 'https://google.com',
});
```
#### User Experience
Users will see a modal inside the Discord app notifying them whether or not they want to proceed. By clicking ***Trust this Domain***, users will not see a modal for that specific domain again.
***
## Open Invite Dialog
Getting an Application Channel Invite, as outlined in [these docs](/developers/resources/invite#get-invite), is not granted by any OAuth scopes. Nonetheless, the `openInviteDialog` command is available via the SDK. This command opens the Application Channel Invite UI within the discord client without requiring additional OAuth scopes.
This command returns an error when called from DM (Direct Message) contexts, so should only be called in Guild Voice or Text Channels. Similarly, this command returns an error if the user has invalid permissions for the channel, so using `getChannelPermissions` (requires OAuth scope `'guilds.members.read'`) is highly recommended.
#### Usage
```javascript theme={"system"}
import {DiscordSDK, Permissions, PermissionUtils} from '@discord/embedded-app-sdk';
const discordSdk = new DiscordSDK(clientId);
await discordSdk.ready();
try {
const {permissions} = await discordSdk.commands.getChannelPermissions();
if (PermissionUtils.can(Permissions.CREATE_INSTANT_INVITE, permissions)) {
await discordSdk.commands.openInviteDialog();
// successfully opened dialog
} else {
console.warn('User does not have CREATE_INSTANT_INVITE permissions');
}
} catch (err) {
// failed to fetch permissions or open dialog
console.warn(err.message);
}
```
User Experience
Users will see a modal inside the Discord app allowing them to send an invite to a channel, friend, or copy an invite link to share manually.
***
## Open Share Moment Dialog
The easiest way for an application to share media to a channel or DM is to use the `openShareMomentDialog` command. This command accepts a Discord CDN `mediaUrl` (eg `https://cdn.discordapp.com/attachments/...`) and opens a dialog on the discord client that allows the user to select channels, DMs, and GDMs to share to. This requires no additional OAuth scopes, but does require the application to be authenticated.
Since `mediaUrl` must be a Discord CDN URL, it is encouraged to use the activities attachment API endpoint (`discord.com/api/applications/${applicationId}/attachment`) to create an ephemeral CDN URL. This endpoint accepts bearer tokens for any scopes, so it can be called from the application client using the authorized user's bearer token. The endpoint returns a serialized attachment, which includes a `url` attribute, which should then be passed to the DiscordSDK command as `mediaUrl`.
#### Usage
```javascript theme={"system"}
import {discordSdk} from './wherever-you-initialize-your-sdk';
import {accessToken} from './wherever-you-store-your-access-token';
// some image
const imageURL = 'https://i.imgur.com/vaSWuKr.gif';
// get image data
const response = await fetch(imageURL);
const blob = await response.blob();
const mimeType = blob.type;
// image data as buffer
const buf = await blob.arrayBuffer();
// image as file
const imageFile = new File([buf], 'example.gif', {type: mimeType});
const body = new FormData();
body.append('file', imageFile);
const attachmentResponse = await fetch(`${env.discordAPI}/applications/${env.applicationId}/attachment`, {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
},
body,
});
const attachmentJson = await attachmentResponse.json();
// mediaUrl is an ephemeral Discord CDN URL
const mediaUrl = attachmentJson.attachment.url;
// opens dialog in Discord client
await discordSdk.commands.openShareMomentDialog({mediaUrl});
```
User Experience
***
## Setting Up an Entry Point Command
An [Entry Point command](/developers/interactions/application-commands#entry-point-commands) is required for users to be able to launch your Activity from the [App Launcher menu](https://support.discord.com/hc/articles/21334461140375-Using-Apps-on-Discord#h_01HRQSA6C8TRHS722P1H3HW1TV) in Discord.
When you enable Activities in your [app's settings](http://discord.com/developers/applications), a [default Entry Point command](/developers/interactions/application-commands#default-entry-point-command) is automatically created for your app. The default Entry Point command will use the `DISCORD_LAUNCH_ACTIVITY` (`2`) [handler type](/developers/interactions/application-commands#application-command-object-entry-point-command-handler-types), which means that Discord automatically launches your Activity for the user and posts a follow-up message into the channel where it was launched from.
If you want to handle sending messages yourself, you can update the handler to be `APP_HANDLER` (`1`). Details about Entry Point command handlers is in the [Entry Point command documentation](/developers/interactions/application-commands#entry-point-handlers).
#### Customizing the Default Entry Point Command
Entry Point commands can be customized in the same way as other [commands](/developers/interactions/application-commands). Since Entry Point commands can only be [global](/developers/interactions/application-commands#making-a-global-command), you'll use the HTTP endpoints for global commands:
* **Edit your existing Entry Point command's name or details** using the [Edit Global Application Command](/developers/interactions/application-commands#edit-global-application-command) endpoint. If you don't know the ID for your app's Entry Point command, use the [Get Global Application Commands](/developers/interactions/application-commands#get-global-application-commands) endpoint to retrieve it.
* **Make a different (option-less) command your Entry Point command** by updating its [command `type`](/developers/interactions/application-commands#application-command-object-application-command-types) to `PRIMARY_ENTRY_POINT` (type `4`). Your app can only have one Entry Point command, so if your app already has one, you must first [delete](/developers/interactions/application-commands#delete-global-application-command) it or [update](/developers/interactions/application-commands#edit-global-application-command) its [command `type`](/developers/interactions/application-commands#application-command-object-application-command-types).
#### Creating an Entry Point Command
To create a new Entry Point command, you can call the [Create Global Application Command](/developers/interactions/application-commands#create-global-application-command) endpoint and set the [command `type`](/developers/interactions/application-commands#application-command-object-application-command-types) to `PRIMARY_ENTRY_POINT` (type `4`).
Your command payload may look something like this:
```json theme={"system"}
{
"name": "launch",
"description": "Launch Realms of Wumpus",
// PRIMARY_ENTRY_POINT is type 4
"type": 4,
// DISCORD_LAUNCH_ACTIVITY is handler value 2
"handler": 2,
// integration_types and contexts define where your command can be used (see below)
"integration_types": [0, 1],
"contexts": [0, 1, 2]
}
```
In addition to the `type` and `handler` values, the command payload includes `integration_types` and `contexts` which let you configure when and where your command can be used:
* `integration_types` defines the [installation contexts](/developers/resources/application#installation-context) where your command is available (to a server, to a user's account, or both). If you don't set `integration_types` when creating a command, it will default to your app's [currently-supported installation contexts](/developers/resources/application#setting-supported-installation-contexts).
* `contexts` defines the [interaction contexts](/developers/interactions/receiving-and-responding#interaction-object-interaction-context-types) where a command can be run in Discord (in a server, in a DM with your app, and/or in DMs and Group DMs with other users).
Details about both of these fields are in the [command contexts](/developers/interactions/application-commands#contexts) documentation.
***
## Encourage Hardware Acceleration
Activities that are compute intensive may benefit from encouraging users to enable hardware acceleration. When an application invokes the `encourageHardwareAcceleration` command the current status of the setting will be returned and the user will be prompted to update the setting, if applicable.
Users will see a modal inside the Discord app if Hardware Acceleration is disabled, encouraging them to change the setting. By clicking **Don't show me this again** they will not see the modal for *any application* on this device again.
#### Best Practices
Switching the Hardware Acceleration setting causes the Discord client to quit and re-launch, so it is best practice to invoke this command as soon as possible, so users do not begin the experience of an application before restarting. Ideally, this is immediately after `await discordSdk.ready()`.
#### Usage
```javascript theme={"system"}
import {DiscordSDK} from '@discord/embedded-app-sdk';
const discordSdk = new DiscordSDK(clientId);
await discordSdk.ready();
const {enabled} = await discordSdk.commands.encourageHardwareAcceleration();
console.log(`Hardware Acceleration is ${enabled === true ? 'enabled' : 'disabled'}`);
```
#### User Experience
# How Activities Work
Source: https://docs.discord.com/developers/activities/how-activities-work
Understand the technical architecture and lifecycle of Discord Activities.
Activities are web applications that run in an iframe within Discord on desktop, mobile and web. In order to achieve this, we use the [`postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) protocol to enable secure communication between your application and Discord.
The [Embedded App SDK](https://github.com/discord/embedded-app-sdk) simplifies this process by managing the `postMessage` protocol on your behalf. For details on available commands and their usage, consult the [SDK Reference](/developers/developer-tools/embedded-app-sdk). Our [Sample Projects](/developers/activities/overview#sample-projects) provide practical examples of how to implement these features.
## Launching Activities
After you have Activities enabled in your Application's [Activity's settings](https://discord.com/developers/applications/select/embedded/settings), your app can launch Activities in two ways:
1. When a user invokes your app's Entry Point command in the App Launcher
2. By responding to an interaction with the `LAUNCH_ACTIVITY` callback type
Each of these are covered in more detail in the below sections.
### Entry Point Command
Activities are primarily opened when users invoke your app's [Entry Point command](/developers/interactions/application-commands#entry-point-commands) in the App Launcher.
When you enable Activities for your app, a [default Entry Point command](/developers/interactions/application-commands#default-entry-point-command) called "Launch" is created for you. By default, Discord automatically handles opening your Activity when your Entry Point command is run by a user.
Read more about setting up Entry Point commands in the [development guide](/developers/activities/development-guides/user-actions#setting-up-an-entry-point-command).
### Interaction Response
Activities can be launched in response to [command](/developers/interactions/overview#commands), [message component](/developers/interactions/overview#message-components), and [modal submission](/developers/interactions/overview#modals) interactions. To open an Activity, set the callback type to `LAUNCH_ACTIVITY` (type `12`) when [responding to the interaction](/developers/interactions/receiving-and-responding#responding-to-an-interaction).
***
## Designed for Single-Page Apps (SPAs)
This SDK is intended for use by a single-page application. We recognize developers may be using frameworks or approaches that are not an exact fit for single-page applications. We recommend nesting those frameworks inside your Activity's top-level single-page application and passing messages as you see fit. Please refer to the [Nested Messages App](/developers/activities/overview#sample-projects) sample project for guidance on this approach.
## Activity Lifecycle
1. **Initialization:** When your iframe is loaded within Discord, it will include unique query parameters in its URL. These parameters are identifiable by your application using the Discord SDK.
2. **Handshake Process:** Constructing the SDK instance begins a handshake process with the Discord client. Once the connection is established, the iframe receives a `[FRAME, {evt: 'READY', ...}]` message. The `ready()` method of the SDK instance resolves once a successful connection has been established.
3. **Authorization and Authentication:** After receiving the `READY` payload, your application should perform authorization and authentication to acquire necessary permissions (scopes). This step is crucial for utilizing specific features or scopes, such as `rpc.activities.write`.
4. **Interacting with Discord Client:** Post-authentication, your application can subscribe to events and send commands to the Discord client. Note that attempting to use commands or subscribe to events outside your granted scope will result in errors. Adding new scopes may prompt an OAuth modal for user permission re-confirmation.
5. **Disconnection and Errors:** Receiving a `[CLOSE, {message: string, code: number}]` message indicates an error or a need to restart the connection process.
6. **Sending Errors or Close Requests:** To communicate an error or request a close from the Discord client, send `[CLOSE, {message?: string, code: number}]`. A code other than CLOSE\_NORMAL will display the message to the user, while CLOSE\_NORMAL results in a silent closure.
## Sample Code and Activity Lifecycle Diagram
Below is a minimal example of setting up the SDK. Please see our [Sample Projects](/developers/activities/overview#sample-projects) for more complete sample applications.
```javascript theme={"system"}
import {DiscordSDK} from '@discord/embedded-app-sdk';
const discordSdk = new DiscordSDK(YOUR_OAUTH2_CLIENT_ID);
async function setup() {
// Wait for READY payload from the discord client
await discordSdk.ready();
// Pop open the OAuth permission modal and request for access to scopes listed in scope array below
const {code} = await discordSdk.commands.authorize({
client_id: YOUR_OAUTH2_CLIENT_ID,
response_type: 'code',
state: '',
prompt: 'none',
scope: ['identify'],
});
// Retrieve an access_token from your application's server
const response = await fetch('/.proxy/api/token', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
code,
}),
});
const {access_token} = await response.json();
// Authenticate with Discord client (using the access_token)
auth = await discordSdk.commands.authenticate({
access_token,
});
}
```
This diagram illustrates the communication flow between your application and Discord in the sample code above.
# Activities Overview
Source: https://docs.discord.com/developers/activities/overview
Learn about Discord Activities - multiplayer games and social experiences.
Activities are web apps hosted in an iframe that use the [Embedded App SDK](/developers/activities/overview#embedded-app-sdk) to communicate with Discord clients. For an introduction to what Activities are, see the [Activities](/developers/platform/activities) platform page.
This section covers how Activities are built. You can explore real-world Activities by reading some of our [developer case studies](https://discord.com/build#case-studies), or jump right into building using the resources below.
Follow the guide to build your first Activity using the Embedded App SDK.
Explore common development patterns and practices to make building Activities simpler.
Learn more about the lifecycle of Activities and how they run in Discord clients.
***
## Developing Activities
Whether you're developing a multiplayer game, a new social experience, or another creative idea, your Activity will be built as a web app that is run in an iframe in Discord on desktop, mobile, and web. You can get started by following the guide on [Building an Activity using the Embedded App SDK](/developers/activities/building-an-activity) or exploring the [sample projects](/developers/activities/overview#sample-projects)
The sections below provide an overview of the Embedded App SDK, but technical details about how Activities are run in Discord is covered in [How Activities Work](/developers/activities/how-activities-work).
### Embedded App SDK
The [Embedded App SDK](/developers/developer-tools/embedded-app-sdk) handles all of the communication between Discord and your app, making it easier to do common tasks like managing connected users, supporting mobile clients, and debugging your Activity.
The Embedded App SDK offers different events and commands to handle the communication between Discord and your Activity, which are covered more below.
#### Commands
The SDK has a set of commands you can call to interact with a Discord client, given you have the appropriate scopes. This is helpful when you want to do authorize and authenticate users, fetch information about your Activity, or update information for your Activity or an authenticated user.
Read the [Embedded App SDK documentation](/developers/developer-tools/embedded-app-sdk#sdk-commands) for a full list of commands, and details about each command.
#### Events
The SDK also has events you can subscribe (or unsubscribe) to for things happening in a connected client that are relevant to your Activity, given you have the appropriate scopes. This is helpful when you want to do things like handle initial connection and thrown errors, listen to updates about a connected user, and listen to events related to your Activity instance.
Read the [Embedded App SDK documentation](/developers/developer-tools/embedded-app-sdk#sdk-events) for a full list of events, and details for each event.
***
## Sample Projects
Use the sample projects we've built to help you get started building quickly and learn from common development patterns.
You can also find a list of community-maintained samples on [GitHub](https://github.com/discord/embedded-app-sdk-examples?tab=readme-ov-file#community-curated-examples), which includes more framework-specific examples.
This TypeScript starter template is perfect for getting your own Activity up and running quickly.
This reference example implements the commands and events available to you within the Embedded App SDK.
This reference example demonstrates an Activity using a nested framework like a game engine.
# Discord Bots & Companion Apps
Source: https://docs.discord.com/developers/bots/overview
Start building bots and companion apps for Discord.
Bots are applications that connect to Discord using the Discord API. They appear in servers as users with an `APP` tag and can interact with members, respond to events, and perform actions programmatically. For a deeper look at what bots can do and when to use one, see the [Bots & Companion Apps](/developers/platform/bots) platform page.
## Getting Started
To build a bot, you'll need to:
* **Create an application** in the Discord Developer Portal. This is where your bot token, OAuth2 credentials, and configuration live.
* **Add a bot user** to your application from the Bot tab. This generates the token your code uses to authenticate.
* **Invite the bot** to a server using an OAuth2 URL with the bot scope and the permissions your bot requires.
* **Connect to the API** using the Gateway for real-time events, the HTTP API for REST operations, or both.
Most developers use a community-maintained library in their language of choice rather than interfacing with the API directly. Official and community libraries are listed in the [Community Resources](/developers/developer-tools/community-resources) page.
If you're ready to dive in, the [Getting Started guide](/developers/quick-start/getting-started) walks through building and running your first bot end to end.
## Building Your Bot
* **[Interactions](/developers/interactions/overview)** — Slash commands, context menu commands, buttons, select menus, and modals that let users interact with your bot.
* **[Components](/developers/components/overview)** — Layout, content, and interactive UI elements you can attach to messages and modals.
* **[Monetization](/developers/monetization/overview)** — Add subscriptions and one-time purchases to your bot using Discord's built-in payment flow.
* **[Discovery](/developers/discovery/overview)** — Get your app listed in the App Directory and App Launcher so users can find and install it.
* **[Guides & Tutorials](/developers/guides/bots)** — Practical guides on implementing common bot features and best practices.
}>
Step-by-step guide to building and running your first Discord bot.
}>
Explore guides on implementing common bot features and best practices.
# Change Log
Source: https://docs.discord.com/developers/change-log
Discover the latest updates, new features, and bug fixes on the Discord Developer Platform. To join in on the conversation, join the [Discord Developers Server](https://discord.com/invite/discord-developers).
## Deprecation: Riot Games Connections in `Get Current User Connections`
The new Riot Games connection is powered by the [Discord Social SDK](/developers/discord-social-sdk/overview) and application identities. As a result, newly created Riot Games connections are no longer returned by the [`Get Current User Connections`](/developers/resources/user#get-current-user-connections) endpoint (`GET /users/@me/connections`).
Starting July 10, 2026, legacy Riot Games and League of Legends connections will also no longer be returned by this endpoint. There is no replacement for this functionality.
## Discord Social SDK Release 1.9.17379
A new release of the Discord Social SDK is now available, with the following updates:
### Stability
* Fixed a crash caused by re-entrant HTTP requests during connection teardown on PlayStation 5.
## Discord Social SDK Release 1.9.17228
A new release of the Discord Social SDK is now available, with the following updates:
### Authentication
* Fixed an issue where `AUTHORIZE_REQUEST` events would not re-subscribe after Discord disconnected and reconnected while the SDK was already running.
### Noise Suppression
* Krisp noise suppression binaries and model files are now included in Unity and Unreal plugin packages for Windows, macOS, Android, and iOS.
## Attachment Editing and `is_spoiler` Param
Apps can now edit the `description` and `is_spoiler` parameters on existing attachments when editing messages.
We have also separated the [attachment request](/developers/resources/message#attachment-object-attachment-request-structure) object in the docs to make it more clear which params you can set for attachments and which fields are provided by the API.
Attachments can be marked as a spoiler by setting `is_spoiler: true` in the attachment request object, and apps can read whether an existing attachment is spoilered by checking for the `IS_SPOILER` [attachment flag](/developers/resources/message#attachment-object-attachment-flags) on the attachment object. The legacy `SPOILER_` filename prefix will continue to mark attachments as a spoiler, but not all spoilered attachments have the prefix.
## Documentation Fix: Subscription Status Values
The integer values for the `INACTIVE` and `ENDING` subscription statuses were previously swapped in the documentation. The correct values are `INACTIVE` = `1` and `ENDING` = `2`, as shown in the [Subscription Statuses](/developers/resources/subscription#subscription-statuses) reference. This has been corrected in both the reference and the [Implementing App Subscriptions](/developers/monetization/implementing-app-subscriptions) guide, which described the subscription lifecycle with the swapped values.
The API has always returned these values, only the documentation was incorrect. If your app branches on the numeric `status` value, double-check that `1` is treated as `INACTIVE` and `2` as `ENDING`.
We discovered this issue while reconciling our documentation against [our OpenAPI spec](https://github.com/discord/discord-api-spec) in [this commit](https://github.com/discord/discord-api-spec/commit/cdbd53a2ac5144536bfee2a34e6d0de13e08eec0).
## Changes to Privileged Intent Access for Discord Apps
Today, we're [announcing changes to how Discord Apps access Privileged Intent](https://support-dev.discord.com/hc/articles/40281523410967) with a new user-based threshold for when access requires review and an annual process to reapply for continued access.
> If your app is accessible to fewer than 10,000 users, these changes do not impact your app.
### What's Changing
#### 1. The review threshold is now based on user count, not server count
Previously, apps in fewer than 100 servers could access Privileged Intents by toggling them on in the Developer Portal, and apps in 100+ servers needed to apply for access.
Starting today, the threshold is based on the number of users your app can access across all the servers it belongs to. If your app has fewer than 10,000 users, you can continue accessing Privileged Intents by toggling them on in the Developer Portal. **Once your app reaches 10,000 users, you'll need to apply for Privileged Intent access**.
#### 2. Apps must now reapply annually for continued access
Apps with Privileged Intent access granted from a prior review will need to reapply to confirm their continued access once per year in the Developer Portal.
#### 3. Apps can keep growing during Privileged Intent review
Under the new user-based threshold, apps can continue joining servers and reaching new users while their submission is under review.
#### 4. App Verification and Privileged Intent review are now separate
Previously, [App Verification](https://support-dev.discord.com/hc/en-us/articles/23926564536471-How-Do-I-Get-My-App-Verified) and Privileged Intent review were part of the same review process. With this change, we have separated App Verification and the Privileged Intent review process.
### What This Means For Your App
For more information on how this applies to your Discord Apps, please read the [Privileged Intents Change announcement article](https://support-dev.discord.com/hc/articles/40281523410967) in our developer help center.
### Updated Documentation & Resources
* Updated [Privileged Intents documentation](/developers/events/gateway#privileged-intents) to reflect the new threshold and review process.
* New [Getting Started with Privileged Intents Review](/developers/gateway/getting-started-with-privileged-intent-review) guide.
* New [You Might Not Need a Privileged Intent](/developers/gateway/you-might-not-need-a-privileged-intent) guide to help you determine whether you need a privileged intent or whether an alternative approach will work for your use case.
## Discord Social SDK Release 1.9.16441
A new release of the Discord Social SDK is now available, with the following updates:
### Playstation 5
* Fixed crash caused by use-after-free in WebSocket callbacks
## New Lobby HTTP API Endpoints
We've expanded the Discord [Lobby resource](/developers/resources/lobby) reference with several public endpoints:
* **[Create or Join Lobby](/developers/resources/lobby#create-or-join-lobby)** (`PUT /lobbies`) — create a lobby for your application or join an existing one by `secret` in a single call.
* **[Send Lobby Message](/developers/resources/lobby#send-lobby-message)** (`POST /lobbies/{lobby.id}/messages`) — send a message into a lobby on behalf of an authenticated member.
* **[Get Lobby Messages](/developers/resources/lobby#get-lobby-messages)** (`GET /lobbies/{lobby.id}/messages`) — fetch up to 200 of the most recent messages from a lobby the calling user is a member of.
* **[Create Lobby Channel Invite for Self](/developers/resources/lobby#create-lobby-channel-invite-for-self)** and **[for User](/developers/resources/lobby#create-lobby-channel-invite-for-user)** — generate a single-use, one-hour guild invite to a lobby's linked channel.
## Discord Social SDK Release Cadence & Support
We've published a new [Release Cadence & Support](/developers/discord-social-sdk/core-concepts/release-cadence-and-support) page for the Discord Social SDK. It documents:
* Our new three-per-year minor release cadence (April, July, November)
* A new support window covering the five most recent minor versions, with hotfix patches (bug fixes and security fixes) for older supported minor releases
* How new Xbox and PlayStation OS SDK supports ship in new Social SDK releases only — each release bundles roughly the last 18 months of console OS SDKs
* The new end-of-life process for older minor releases
### What this means for you
* **Over a year and a half of support per minor release.** Three minor releases of the Social SDK per year combined with a 5-version support window means every minor release you ship with gets more than 18 months of patch coverage.
* **A predictable release calendar.** Releases land in April, July, and November, so you can plan Social SDK upgrades and console certification windows well in advance.
* **More bandwidth for new features and stability.** A defined support window lets us focus engineering effort where it matters most, which means more capacity to build new features in current minor releases and keep the versions in the support window robust and reliable.
### What changes with 1.10
These changes take effect with the 1.10 release, shipping in **July 2026**:
* The supported version window will cover versions **1.6 through 1.10**. Versions **1.5 and earlier** will be end-of-life and no longer eligible for patch updates.
* **New Xbox and PlayStation OS SDK support will ship in new Social SDK releases only**, not as patches to older Social SDK versions. Each Social SDK release will bundle roughly the last 18 months of console OS SDK versions.
**Already-shipped Social SDK releases (1.6–1.9) are unchanged** — they keep the console OS SDK support they were originally built with. The new model applies from 1.10 forward.
We're sharing this ahead of the 1.10 release so developers on older versions have plenty of time to upgrade.
## Account Linking Failure Modes
We've expanded documentation across the Social SDK guides to cover what happens when the Discord-to-game account link is severed — whether by a Discord account ban, an explicit unmerge, or an OAuth2 token revocation. Highlights:
* **[Ban-Driven Unmerge](/developers/discord-social-sdk/development-guides/using-provisional-accounts#ban-driven-unmerge)** documents the full lifecycle when a user's Discord account is banned: tokens are deleted immediately, a new cross-platform-restricted provisional account is created carrying the original external identity, and merge attempts return error [`530017`](/developers/discord-social-sdk/development-guides/using-provisional-accounts#merge-request-failures) until the ban lifts (or, for permanent bans, indefinitely).
* **[Out-of-Band Revocation](/developers/discord-social-sdk/development-guides/account-linking-with-discord#out-of-band-revocation)** describes the auth-side observable signals (`APPLICATION_DEAUTHORIZED` webhook + `invalid_grant` on refresh) when a user removes your app from Discord or has their account banned.
* **[When Refresh Fails](/developers/discord-social-sdk/development-guides/account-linking-with-discord#when-refresh-fails)** documents the `/oauth2/token` response when the underlying token has been deleted, and recommends treating `invalid_grant` as equivalent to receiving the webhook.
## Discord Social SDK Release 1.9.15780
A new release of the Discord Social SDK is now available, with the following updates:
### Xbox
* Added Xbox GDK 260400 support
### Noise Suppression
* Integrated Krisp noise suppression across Android, iOS, macOS, and Windows
## New `flags_new` Field on Application Object
The [Application object](/developers/resources/application#application-object) now includes a `flags_new` field — a string-serialized integer containing the full set of [application flag bits](/developers/resources/application#application-object-application-flags), including any new flag bits introduced beyond bit 30.
The existing `flags` field continues to be serialized as a 32-bit integer. Existing integrations consuming `flags` for bits at or below `2^30` are not impacted, and request payloads that include flag values should continue to use the original `flags` field.
This mirrors the [`permissions_new` / `allow_new` / `deny_new`](/developers/topics/permissions) pattern previously introduced for permission bitfields.
## User Premium Type Field Now Requires A Scope
The `premium_type` field on the [User object](/developers/resources/user#user-object-user-structure) will return `0` for applications that have not been approved for the `identify.premium` scope. To receive Nitro subscription level, you must request the `identify.premium` scope during OAuth authorization. Without this scope, the field returns `0` regardless of the user's actual subscription status.
The `identify.premium` scope is only available to approved partners.
## Voice Muting Based on Player Blocks
We've published a new how-to guide covering how to implement bi-directional voice muting in lobby voice calls based on block relationships. The guide covers two approaches for populating per-member mute lists — client-side using any client-accessible block signal (Discord relationships, in-game block lists, etc.) and server-side via lobby member metadata set at lobby creation — and a shared client-side implementation that applies mutes in both directions.
As part of this work, we've also documented the [`POST /lobbies/{lobby.id}/members/bulk`](/developers/resources/lobby#bulk-update-lobby-members) endpoint, which allows adding, updating, or removing up to 25 lobby members in a single request.
[Voice Muting Based on Player Blocks](/developers/discord-social-sdk/how-to/voice-muting-for-blocked-players)
## Discord Social SDK Release 1.9.15332
A new release of the Discord Social SDK is now available, with the following updates:
### Playstation 5
* Added OS 13.000 support
### Activity Invites
* Accepting an activity invite over RPC now uses the `application_id` from the invite when provided, fixing cases where invites from sibling applications were sometimes attributed to the wrong app. This only affects teams leveraging Publisher Level Account Linking for having a single auth flow across all the games in their portfolio.
## Proximity Voice Chat Guide for Game Developers
We've published a new guide on adding proximity voice chat to a multiplayer game using the Discord Social SDK and Unity. The guide walks through the general architecture and the concepts are adaptable to any game or engine.
[Add proximity voice chat to your game](/developers/game-development/how-to-add-proximity-voice-chat-to-your-game)
## Security Updates for Provisional Account OIDC Integrations
We've made security improvements to how Discord validates OIDC issuer URLs and fetches OIDC configuration for [provisional accounts](/developers/discord-social-sdk/development-guides/using-provisional-accounts) — including stricter hostname checks and blocking HTTP redirects during configuration and JWKS discovery.
**Existing provisional account OIDC integrations are not affected** and will continue to work without any changes on your part.
However, if you need to edit your OIDC configuration in the [Developer Portal](https://discord.com/developers/applications/select/social-sdk/external-auth-providers), your updated configuration will be validated against these requirements. We've published new documentation covering the full set of [OIDC Integration Requirements](/developers/discord-social-sdk/development-guides/using-provisional-accounts#oidc-integration-requirements) — including issuer URL rules, discovery document requirements, supported signing algorithms, and ID token claims. Before making any changes to a production integration, review those requirements and verify your configuration works in a test application first.
## Voice Channel Status and Start Time Documentation
Voice channel status has been available for some time, but we're now officially documenting the endpoint, permission, and related Gateway and audit log events so apps can integrate with it.
The [Set Voice Channel Status](/developers/resources/channel#set-voice-channel-status) endpoint (`PUT /channels/{channel.id}/voice-status`) allows apps to set a short string (up to 500 characters) describing what's happening in a voice channel.
### Permission
Setting a channel's status requires the `SET_VOICE_CHANNEL_STATUS` permission (`1 << 48`), now documented in the [permissions](/developers/topics/permissions) table. If the bot user is not connected to the voice channel, the `MANAGE_CHANNELS` permission is additionally required.
### Gateway Events
Voice channel status and voice session start time are ephemeral and are not included on the [channel](/developers/resources/channel#channel-object) object. To read them, apps send the [Request Channel Info](/developers/events/gateway-events#request-channel-info) Gateway command (opcode `43`) and receive a [Channel Info](/developers/events/gateway-events#channel-info) event containing the requested fields (currently `status` and `voice_start_time`) for channels in the guild.
Two receive events are dispatched when these fields change:
* [Voice Channel Status Update](/developers/events/gateway-events#voice-channel-status-update) — fired when a channel's status changes.
* [Voice Channel Start Time Update](/developers/events/gateway-events#voice-channel-start-time-update) — fired when a voice session's start time changes.
### Audit Log
Two [audit log events](/developers/resources/audit-log#audit-log-events) track status changes: `VOICE_CHANNEL_STATUS_UPDATE` (`192`) and `VOICE_CHANNEL_STATUS_DELETE` (`193`). A `status` field in [Optional Audit Entry Info](/developers/resources/audit-log#audit-log-entry-object-optional-audit-entry-info) carries the new status on `VOICE_CHANNEL_STATUS_UPDATE` entries.
## Deprecation: OAuth2 Refresh Token Grant for OIDC Provisional Accounts
If you are using [OIDC server-side authentication](/developers/discord-social-sdk/development-guides/using-provisional-accounts#server-authentication-with-external-credentials-exchange)
for provisional accounts, a `refresh_token` is returned — but using it via the OAuth2 `refresh_token` grant is now
deprecated.
This brings OIDC in line with all other provisional account authentication methods: when the token
expires, re-authenticate using a fresh provider token and pass the new access token to [`Client::UpdateToken`].
See the [Refreshing Provisional Account Tokens](/developers/discord-social-sdk/development-guides/using-provisional-accounts#refreshing-provisional-account-tokens)
section for details.
[`Client::UpdateToken`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a606b32cef7796f7fb91c2497bc31afc4
## Message Forwarding Requires Message Content Access
Starting today, applications must be able to read a message's content in order to forward it.
An application attempting to forward a message it cannot read will now receive error code `160014` with the message "You cannot forward a message whose content you cannot read."
Most applications will not be affected by this change, but those recently affected have been notified directly via system DM.
## Discord Social SDK Release 1.8.14985
A new release of the Discord Social SDK is now available, with the following updates:
### Xbox
* Added Xbox GDK 251002 support
## RPC over IPC Documentation
Documentation for Discord's RPC over IPC (Inter-Process Communication) transport is now available. IPC is a transport for native applications to communicate with a local Discord client, offering high-performance local communication without network overhead.
The [RPC documentation](/developers/topics/rpc) covers:
* IPC socket paths for Windows and Linux/macOS
* Handshake flow and opcodes
* IPC-specific events (`READY`, `ERROR`, and `GUILD_STATUS`)
If you're building a game that integrates Discord social features, we recommend using the [Discord Social SDK](/developers/discord-social-sdk/overview) instead of communicating directly over RPC.
## Discord Social SDK Release 1.8.14856
A new release of the Discord Social SDK is now available, with the following updates:
### iOS Unity & Unreal
* Unity and Unreal packages now include Debug plugins, which have support for logging at the Info and Verbose severity levels
### Party Privacy
* Fixed bug in which changes to party privacy settings didn't propagate when calling [`Client::UpdateRichPresence`], meaning switching a party from private to public wouldn't update the join button in Discord
## Search Guild Messages
We've added documentation for the [Search Guild Messages](/developers/resources/message#search-guild-messages) endpoint, which allows bots to search for messages within a guild using a variety of filters.
The endpoint supports filtering by content, author, channel, mentions, attachments, embeds, and more. Results can be sorted by timestamp or relevance.
This endpoint requires the `READ_MESSAGE_HISTORY` permission and the `MESSAGE_CONTENT` [Privileged Intent](/developers/events/gateway#privileged-intents).
The error code `110000` has been added to the [error codes](/developers/topics/opcodes-and-status-codes#json) table to reflect the index-not-ready response returned when a guild has not yet been indexed for search.
## Discord Social SDK Release 1.8.14587
A new release of the Discord Social SDK is now available, with the following updates:
### Debug Support
* Added Debug library support for the following:
* Unity plugin & sample
* Unreal plugin & sample
* Windows, Mac, Linux libraries
* Android library
* PS5 11.000 & 12.000 library
* PS4 12.000 & 12.500 library
* Scarlett & Xbox One 250400 GDK library
## Developer Portal & Docs Refresh
The Developer Portal and Developer Docs have both been updated with improved navigation, usability, and a redesigned theme, with more developer experience improvements on the way. Latest changes include:
### Developer Portal
* **New [dev portal home page](https://discord.com/developers/home)** with quick access to apps, docs, and new developer videos.
* **New theme** with updated navigation and updated light/dark support.
* **New Localization support!** This will use the same language you have set in the Discord client.
### Developer Docs
* **Reorganized structure** with new tabs for each feature area and new sidebars for easier navigation.
* **New guides and overviews** for platform-wide features and use cases, with more on the way!
* **AI & MCP support:** Docs now work with AI development tools via Copy-as-Markdown, an LLMs.txt file, and an MCP server, available at `https://docs.discord.com/mcp`
* **Existing docs URLs** are stable and were preserved during the reorganization. Some titles and content were updated or moved for clarity.
### What's Next
We are working on more improvements for the portal and docs. We invite you to share any issues or feedback in the [Next Gen Docs discussion thread](https://github.com/discord/discord-api-docs/discussions/categories/next-gen-docs-feedback).
## Discord Social SDK Release 1.8.14437
A new release of the Discord Social SDK is now available, with the following updates:
### Security
* Filter the bearer token in all levels of logging
* Release builds only emit log statements for Error or Warning levels of severity
* Debug builds will still have logs for Info and Verbose levels of severity
* Added a DEBUG BUILD Warning at the top of the debug log that indicates it should not be used in production
## Discord Social SDK Release 1.7.14433
A new release of the Discord Social SDK is now available, with the following updates:
### Security
* Filter the bearer token in all levels of logging
* Release builds only emit log statements for Error or Warning levels of severity
* Debug builds will still have logs for Info and Verbose levels of severity
* Added a DEBUG BUILD Warning at the top of the debug log that indicates it should not be used in production
## Discord Social SDK Release 1.6.14448
A new release of the Discord Social SDK is now available, with the following updates:
### Security
* Filter the bearer token in all levels of logging
* Release builds only emit log statements for Error or Warning levels of severity
* Debug builds will still have logs for Info and Verbose levels of severity
* Added a DEBUG BUILD Warning at the top of the debug log that indicates it should not be used in production
## Updates to Context Menu Commands
We've redone the UI for [User Commands](/developers/interactions/application-commands#user-commands) and [Message Commands](/developers/interactions/application-commands#message-commands) to make it easier to find commands and unlock higher limits!
#### Refreshed UI
The UI has been updated based on feedback! No longer do you have to scroll through a long list of commands:
* Commands are now organized by their application, providing hierarchy
* Frequently used commands are now hoisted to the top for easy access
* A new search bar lets you quickly find the exact command you're looking for
#### A Higher Limit
We've heard your feedback about the 5 command limit feeling too restrictive! With this new UI, we have increased the limit to 15 per type, providing breathing room for your app.
#### Developer Resources
Check out the [Application Commands](/developers/interactions/application-commands) page for details on all command types.
## Server-Side Message Moderation
We've released and documented two new API endpoints that enable you to modify the application-scoped moderation metadata on
both game direct messages and lobby messages.
Combined with the [previously announced](/developers/change-log#discord-social-sdk-release-1813395) [`MessageHandle::ModerationMetadata`], and game direct message and lobby [Webhook Events](/developers/events/webhook-events#event-types),
this enables server-side lobby and game direct message moderation workflows. Your game backend can now evaluate messages and attach moderation metadata that the Discord Social SDK can then deliver to game clients in real time, driving in-game message rendering dependent on moderation results.
To see this in detail, have a look at the newly updated [How To Integrate Moderation](/developers/discord-social-sdk/how-to/integrate-moderation) guide with the full server-side moderation flow.
## Discord Social SDK Release 1.8.14026
A new release of the Discord Social SDK is now available, with the following updates:
### Stabilization
* Fixed an occasional crash when linking your Discord account to your Application while in a Voice Call
## Publisher Level Account Linking
We've published new documentation for **Publisher Level Account Linking**, a feature that enables game publishers with multiple titles to implement a single authorization flow across their entire game portfolio.
Publisher Level Account Linking requires Discord approval.
To inquire about access, speak to your Discord account representative or [contact us](https://discord.com/developers/contact-us)
With Publisher Level Account Linking, players authenticate once through a publisher application instead of separately for each game. This creates a simplified experience for players while giving publishers centralized account management capabilities.
Key features covered in the guide:
* Setting up parent-child application hierarchies between publisher and game applications
* Exchanging parent access tokens for child access tokens
* Integrating child tokens with the Discord Social SDK
* Understanding token lifecycles and revocation
To learn more and get started, check out the [Publisher Level Account Linking guide](/developers/discord-social-sdk/development-guides/publisher-level-account-linking).
## Discord Social SDK Release 1.8.13870
A new release of the Discord Social SDK is now available, with the following updates:
### Playstation 5
* Added OS 12.000 support
### iOS
* Added code stripping to iOS builds to reduce size
## Community Invite Guide
We're launching a guide to showcase the new [invite endpoints](/developers/change-log#new-invite-endpoints) and functionality. It has examples on how to create invites with roles through the UI and API and how to restrict access to an invite using `target_users` through the API. This guide highlights use cases for community servers and walks through steps for creating and sending invites that grant roles or restrict access to specific users.
[Check out the new guide here!](/developers/tutorials/using-community-invites)
## Radio Groups, Checkbox Groups, and Checkboxes in Modals
We're introducing three new modal components: **Radio Groups**, **Checkbox Groups**, and **Checkboxes**. These components expand the ways users can interact with your app through modals, enabling single-choice selections, multi-select options, and simple yes/no toggles.
#### The New Components
* [**Radio Group**](/developers/components/reference#radio-group): A single-selection component for choosing exactly one option from a list. Supports the `required` field to allow optional selections.
* [**Checkbox Group**](/developers/components/reference#checkbox-group): A multi-selection component with configurable `min_values` and `max_values` constraints for flexible selection requirements.
* [**Checkbox**](/developers/components/reference#checkbox): A simple boolean toggle for yes/no style questions.
All three components must be placed inside a [**Label**](/developers/components/reference#label) component and are only available in modals.
#### Developer Resources
* [Using Modal Components](/developers/components/using-modal-components) - Learn how to create modals
* [Component Reference](/developers/components/reference) - Complete documentation for all available components
## Next Generation Docs Project
We're excited to announce the launch of our Next Gen Docs project! This initiative aims to improve the way developers interact with Discord's developer documentation, making it more accessible, comprehensive, and user-friendly.
## Our Goal
The Discord API has evolved far beyond its origins as a platform for bots. Today, we're a comprehensive developer platform supporting:
* **Discord Apps and Bots** - The foundation of our ecosystem, extending Discord's functionality with custom apps, commands, and integrations
* **Discord Activities** - Real-time games and social experiences that users can launch directly inside Discord
* **Social SDK for Games** - Bringing Discord-powered features like voice, chat, rich presence, and social graph to games
Our documentation needs to evolve with us. With this project, we're not just updating our docs. We're aiming to improve the entire developer experience for learning and building on Discord as it exists in 2026 and beyond.
## Our Next Gen Docs Journey
**What we've done so far:**
* Migrated to [Mintlify](https://mintlify.com) for a modern documentation experience
* For this migration, we preserved our existing content and familiar design but plan to evolve this over time
* We no longer have to maintain and build our own documentation platform and can instead focus on making content and improving our developer experience
* Gained new capabilities:
* **PR Previews** - See documentation changes before they go live! This works locally as well as in your pull requests
* **More Components and Docs Features** - We now have access to all of Mintlify's documentation components that will continue to grow over time
* **AI features & improved search** - You can now send our docs to your LLM of choice or access them via MCP (more on this soon!)
If you're reading this, the migration is complete! 🎉
This migration did require us to reorganize the entire repository and make a lot of formatting changes. If you have an open PR, it will be a pretty gnarly merge conflict to resolve. We will be focusing on getting our PR backlog down over the next few weeks.
We combed through the existing documentation to ensure everything was migrated correctly, but if you spot anything that looks off, please let us know by opening an issue or submitting feedback [here](https://github.com/discord/discord-api-docs/discussions/categories/next-gen-docs-feedback). We will be actively monitoring feedback to quickly address any issues.
## Why This Matters
Great documentation isn't just about having the right information, it's about presenting it in a way that helps developers succeed.
In the coming months, we'll be shipping changes to our documentation to ensure that it:
* **Reduces time-to-first-success** for new developers
* **Scales with complexity** as your projects grow
* **Stays current** with our rapidly evolving platform
* **Serves all skill levels** from beginners to experts
## Contributing to Our Vision
This transformation is happening with the developer community at its heart. We welcome:
* **Feedback** on what's working and what isn't
* **Content contributions** through our existing PR process
* **Bug reports** when documentation doesn't match reality
We have created a Github Discussion topic for [Next Gen Docs Feedback](https://github.com/discord/discord-api-docs/discussions/categories/next-gen-docs-feedback) to collect feedback.
See our [Contributing Guidelines](https://github.com/discord/discord-api-docs/blob/main/CONTRIBUTING.md) for how to get involved.
## Community Invites Update
We've updated the community invite endpoints with two changes due to a security concern:
* [Get Target Users](/developers/resources/invite#get-target-users) returns a standardized CSV file with a header `user_id` and each user ID on its own line. If you relied on the header you submitted or weren't reading it from the file you got back you'll need to update to expect only `user_id` as the header in the csv now.
* [Get Channel Invites](/developers/resources/channel#get-channel-invites) returns a partial for [roles](/developers/topics/permissions#role-object) instead of the full role object. This is a breaking change as it used to return the full role object and now only contains `id`, `name`, `position`, `color`, `colors`, `icon`, and `unicode_emoji`.
## Relationships.read scope
We've opened up the `relationships.read` scope for Activities under the [Social SDK terms](https://support-dev.discord.com/hc/en-us/articles/30225844245271-Discord-Social-SDK-Terms). To get access to the scope you will need to accept the Social SDK terms for your app in the [Social SDK settings](https://discord.com/developers/applications/select/social-sdk/getting-started). Requesting approval for this scope from Discord is no longer necessary. With this scope `getRelationships()` in the embedded app SDK will now return a player's relationships.
The Embedded App SDK is available via [npm](https://www.npmjs.com/package/@discord/embedded-app-sdk) and [GitHub](https://github.com/discord/embedded-app-sdk). You can check out our [installation guide and reference](/developers/developer-tools/embedded-app-sdk) to get started with it!
## New Invite Endpoints
We've added new endpoints and functionality allowing invites to grant roles and to only be accepted by specified users. These are perfect for communities that want to manage access more granularly or reward members with special roles when they join a server.
[Create Channel Invite](/developers/resources/channel#create-channel-invite) has been updated to support `target_users_file` and `role_ids` parameters.
* `target_users_file`: A CSV file with user IDs to specify who can accept the invite
* `role_ids`: Role IDs for roles to assign to users when they accept the invite
Invite endpoints:
* [Get Target Users](/developers/resources/invite#get-target-users): Gets the users allowed to see and accept an invite
* [Update Target Users](/developers/resources/invite#update-target-users): Updates the users allowed to see and accept an invite
* [Get Target Users Job Status](/developers/resources/invite#get-target-users-job-status): Checks the status of the job that processes the target users for an invite from file upload
## Discord Social SDK Release 1.8.13395
A new release of the Discord Social SDK is now available, with the following updates:
### Moderation Metadata Support
* \[Coming Soon] Messages will soon support [`MessageHandle::ModerationMetadata`], a set of custom string key-value pairs that can be used
to communicate moderation-related information between your game backend and client.
* This SDK version includes the functionality to support this upcoming feature, but it will only become available
after an upcoming Discord API change.
### WebSocket Reporting
* WebSocket connection failures will now report an HTTP status code to the SDK log.
### PlayStation 5
* Improved accuracy of timing metrics used for monitoring the health of connections and voice calls.
## Discord Social SDK Release 1.7.13357
A new release of the Discord Social SDK is now available, with the following updates:
* Fixed a crash on Steam Deck when calling RegisterLaunchCommand
* Fixed a crash on iOS when Deeplink Authentication fails.
## New Social SDK Guide: Account Linking from Discord
We've added a new guide showing how to enable account linking entry points within the Discord client. With Social SDK 1.6+, Discord can now display "Link your account" prompts and buttons throughout the client to encourage players to connect their game accounts, leading to higher linking rates and better social engagement.
The guide covers two implementation flows:
* **Connected Game Flow (recommended)**: Uses new callback methods to launch account linking directly in your game when players click Discord's entry points
* **Web Flow**: Routes players through a web-based OAuth flow (currently only available for select partners)
[Read the full guide →](/developers/discord-social-sdk/development-guides/account-linking-from-discord)
## Discord Social SDK Release 1.6.13305
A new release of the Discord Social SDK is now available, with the following updates:
* Fixed a crash on Steam Deck when calling RegisterLaunchCommand
* Fixed a crash on iOS when Deeplink Authentication fails.
## Get Guild Role Member Counts Endpoint
Apps can now use the [Get Guild Role Member Counts](/developers/resources/guild#get-guild-role-member-counts) endpoint to access the number of members that have each role!
## Discord Social SDK Release 1.7.13152
A new release of the Discord Social SDK is now available, with the following updates:
### macOS
* Added support for macOS 10.15 to the build. The minimum versions are now 10.15 for Intel Macs and 11.0 for Apple Silicon Macs.
## Permission Changes Going into Effect February 2026 for PIN\_MESSAGES, BYPASS\_SLOWMODE, CREATE\_GUILD\_EXPRESSIONS, and CREATE\_EVENTS
We have some important permission changes that will take effect in February 2026.
The introduction of the following permissions were non-breaking changes.
However, the breaking changes described below - where the old permissions will no longer grant these abilities - will take effect on February 23, 2026. These changes involve a few permissions that we split from their original permission to provide more granular control over bot and user actions.
### What's Changing?
**1. Pin Messages Permission**
* The `PIN_MESSAGES` permission (`1 << 51`) was split from `MANAGE_MESSAGES` on August 20, 2025.
* [Read the change log](/developers/change-log#pin-permission-split).
* Starting February 23, 2026, users and bots will need the `PIN_MESSAGES` permission to pin messages. `MANAGE_MESSAGES` alone will no longer be sufficient.
**2. Bypass Slowmode Permission**
* The `BYPASS_SLOWMODE` permission (`1 << 52`) is being split from `MANAGE_MESSAGES`, `MANAGE_CHANNEL`, and `MANAGE_THREADS`.
* [Read the change log](/developers/change-log#new-bypassslowmode-permission-permission-split).
* Note: This primarily affects users, as bots are not affected by slowmode restrictions.
* Starting on February 23, 2026, users will need the `BYPASS_SLOWMODE` permission to not be affected by slowmode restrictions.
**3. Create Expressions Permission**
* The `CREATE_GUILD_EXPRESSIONS` permission (`1 << 43`) was created in July 2023 and split from `MANAGE_GUILD_EXPRESSIONS` for users in December 2023. [Read the change log](/developers/change-log#clarification-on-permission-splits-for-expressions-and-events).
* As of today, bots now have access to the `CREATE_GUILD_EXPRESSIONS` permission.
* [Read the change log](/developers/change-log#guild-expressions-and-events-permissions-now-available-to-developers).
* Starting on February 23, 2026, bots will need the `CREATE_GUILD_EXPRESSIONS` permission to create custom emoji and stickers. `MANAGE_GUILD_EXPRESSIONS` alone will no longer be sufficient.
**4. Create Events Permission**
* The `CREATE_EVENTS` permission (`1 << 44`) was created in July 2023 and split from `MANAGE_EVENTS` for users in December 2023. [Read the change log](/developers/change-log#clarification-on-permission-splits-for-expressions-and-events).
* As of today, bots now have access to the `CREATE_EVENTS` permission.
* [Read the change log](/developers/change-log#guild-expressions-and-events-permissions-now-available-to-developers).
* Starting on February 23, 2026, bots will need the `CREATE_EVENTS` permission to create scheduled events. `MANAGE_EVENTS` alone will no longer be sufficient.
### What Do You Need to Do?
If your bot performs any of the following actions, please review and update your bot's permission requests before February 23, 2026:
* **Pins messages:** Request the `Pin Messages` permission
* **Creates custom emoji or stickers:** Request the `Create Expressions` permission
* **Creates scheduled events:** Request the `Create Events` permission
These changes are designed to give server administrators more control over what bots and users can do, ensuring better security and permission management.
## New BYPASS\_SLOWMODE Permission & Permission Split
We have introduced a new permission: `BYPASS_SLOWMODE`. This permission allows designated roles or users to bypass slowmode restrictions in channels.
* The `BYPASS_SLOWMODE` permission (`1 << 52`) is being split from `MANAGE_MESSAGES`, `MANAGE_CHANNEL`, and `MANAGE_THREADS`.
* Note: This primarily affects users, as bots are not affected by slowmode restrictions.
* Starting on February 23, 2026, users will need the `BYPASS_SLOWMODE` permission to not be affected by slowmode restrictions.
## Guild Expressions and Events Permissions now available to developers
In 2023, we had introduced permission splits for guild expressions (custom emoji and stickers) and scheduled events. These changes were made to give server administrators more granular control over who can create content in their communities. [Read the change log](/developers/change-log#clarification-on-permission-splits-for-expressions-and-events).
Today, we are announcing that these permissions are now available for bot developers to use and will be required for certain actions starting **February 23, 2026**.
**Create Guild Expressions Permission**
* The `CREATE_GUILD_EXPRESSIONS` permission (`1 << 43`) was created in July 2023 and split from `MANAGE_GUILD_EXPRESSIONS` for users in December 2023.
* As of today, bots now have access to the `CREATE_GUILD_EXPRESSIONS` permission.
* Starting on February 23, 2026, bots will need the `CREATE_GUILD_EXPRESSIONS` permission to create custom emoji and stickers. `MANAGE_GUILD_EXPRESSIONS` alone will no longer be sufficient.
**Create Events Permission**
* The `CREATE_EVENTS` permission (`1 << 44`) was created in July 2023 and split from `MANAGE_EVENTS` for users in December 2023.
* As of today, bots now have access to the `CREATE_EVENTS` permission.
* Starting on February 23, 2026, bots will need the `CREATE_EVENTS` permission to create scheduled events. `MANAGE_EVENTS` alone will no longer be sufficient.
## Discord Social SDK Release 1.7.13024
A new release of the Discord Social SDK is now available, with the following updates:
## Windows
* Fixed default audio device selection to once again prefer the default system audio device instead of the default communications device.
## Xbox
* Added support for GDK 240605 to the build.
## Discord Social SDK Release 1.7
A new release of the Discord Social SDK is now available, with the following updates:
## Authentication
* Sign in with Apple and PlayStation Network are now supported as external identity providers.
## Linked Channels
* The results of [`Client::GetGuildChannels`] are now sorted in the order in which they appear in the Discord client.
Additional properties added to [`GuildChannel`] for type, position and parent category ID.
## PlayStation 5
* Added Low Energy Mode support. Explicit control of SDK thread affinity is provided by `cpuAffinityMask` property of [`ClientCreateOptions`].
* Fixed a crash when destroying and recreating [`Client`].
## Windows
* Fixed an issue which caused local crash dumps to be disabled in processes which load the Social SDK.
## Voice
* The Social SDK now uses our latest audio subsystem on Windows, bringing it more in line with the desktop client. Improves device compatibility and fixes some long-standing issues like Bluetooth devices changing profiles on startup.
* Desktop clients now use WebRTC m130 internally (upgraded from m116).
* Bug fixes and improvements to DAVE E2EE protocol implementation.
## Discord Social SDK Release 1.6.12767
A new release of the Discord Social SDK is now available, with the following update:
Implemented a fix for a crash on Playstation when creating the Discord client, destroying it, and creating another.
## Discord Social SDK Release 1.5.12211
A new release of the Discord Social SDK is now available, with the following update:
## Bug Fix
* Fixed a crash when calling [`Client::GetUserGuilds`] on iOS devices. First occurrence of this crash introduced in 1.5.10839
## Discord Social SDK Release 1.6.12170
A new release of the Discord Social SDK is now available, with the following updates:
## New Features
This small update to v1.6 adds some highly requested features from our partners. We found some time to sneak them in…
* You can now provide custom art to display as a banner image in game invites that appear in Discord. This is done one
of two ways:
* By specifying a URL or an `asset key` to an image in the `activity.assets.inviteCoverImage` parameter when
calling `Client::UpdateRichPresence`. This method even affords you the ability to set a unique image
on each invite if you want!
* By uploading cover image art in the `Rich Presence` tab in the Developer Portal for your Application. Note: This
will be used as the fallback image if you don't specify one via `activity.assets.inviteCoverImage`
* It's now possible to customize the displayed name for your Application in Discord's Rich Presence. To do so, set
the `activity.name` parameter when calling [`Client::UpdateRichPresence`]
* Added support for Microsoft Xbox GDK version 2025.04
* Fixed a crash when calling [`Client::GetUserGuilds`] on iOS devices
## Introducing the File Upload component in Modals
Have you ever wanted to collect more than text from a user through a modal? With the new [**File Upload**](/developers/components/reference#file-upload) component you can! You can specify a min and max number of files accepted between 0 and 10, and if uploading files within that limit is required before submitting. Any file types are accepted, and the max file size is based on the user's upload limit in that channel.
#### The New Component:
* [**File Upload**](/developers/components/reference#file-upload)
#### Developer Resources
* [Using Modal Components](/developers/components/using-modal-components) - Dive into creating a modal
* Check out our [Component Reference](/developers/components/reference) for details on all available components.
## Discord Social SDK Release 1.6
A new release of the Discord Social SDK is now available, with the following updates:
### ✨ New Features
* When calling [`Client::GetGuildChannels`], channels are now sorted by their `position` field, which matches how they are sorted in the Discord client.
* Messages received via the Social SDK, no longer create notifications in a Discord client for the same user, to avoid double notification on the same machine.
**This release adds features to support upcoming Discord experiments that will enhance how games integrate with user profiles and authentication:**
* **Game Profile Integration**: New functionality to display game data on Discord user profiles. This includes `UserApplicationProfile` support with two new methods: `Client::GetUserApplicationProfiles` and `Client::GetUserApplicationProfilesNoLock` on the Users class, which retrieve game identity data from external authentication providers.
* **In-Discord Authentication Flow**: Support for users to start account linking directly from Discord (rather than having to initiate it from within your game). Added [`Client::RegisterAuthorizeRequestCallback`] and [`Client::RemoveAuthorizeRequestCallback`] methods to handle authentication requests that originate from various Discord entry points. These functions support upcoming Discord client experiments that will be gradually rolled out to users over time.
### ⚠️ Deprecations
This deprecation aims to improve consistence across the SDK's API surface as well as provide a safer implementation that has fewer edge cases and less potential for accidental misuse.
* Deprecated [`Client::GetCurrentUser`] API in favor of [`Client::GetCurrentUserV2`] which returns optional values instead of potentially invalid handles.
### 🚀 Performance Improvements
* This update implements caching capabilities for the [`Client::GetUserMessagesWithLimit`] function to avoid unnecessary remote API calls when sufficient messages are already cached locally.
### 🎤 Voice Communications Fixes and Improvements
Fixes several critical bugs with the voice communications system, as well as improved overall reliability, and noise and echo suppression and cancellation.
* Fixed an issue where voice calls would sometimes transition to `Disconnected` state instead of reconnecting properly after a network interruption.
* Extended AGC2 (Automatic Gain Control 2) support to mobile platforms
### 🐛 General Stability and Bug Fixes
Multiple general critical bugs that can cause crashes and panics. We highly recommend upgrading to 1.6 to avoid them in your game.
* Fixed critical bug where activity party privacy wasn't properly set, causing "ask to join" to appear instead of "join" for public parties.
* Fixed critical memory safety issue preventing connection objects from being deallocated during timer callbacks.
* Fixed C# marshaling alignment bugs and double-free crashes.
* Improved gateway resilience with fallback to generic URLs on zonal gateway failures.
* Fixed WebSocket write-after-close errors preventing connection issues.
To learn more about building with the Discord Social SDK, check out the [Discord Social SDK Overview](https://discord.com/developers/docs/discord-social-sdk/overview), and if you have questions, feel free to drop them in [#social-sdk-dev-help](https://discord.com/channels/613425648685547541/1350223314307776592)!
## Adding More Modal Components!
We've added more components to modals! All select menus (User, Role, Mentionable, Channel) are now fully supported in modals. In order to use a select menu in a modal, it must be placed inside a [Label](/developers/components/reference#label) component. We've also added the [Text Display](/developers/components/reference#text-display) component with markdown support as a top-level component in modals.
#### Components Now Supported in Modals:
* [**User Select**](/developers/components/reference#user-select)
* [**Role Select**](/developers/components/reference#role-select)
* [**Mentionable Select**](/developers/components/reference#mentionable-select)
* [**Channel Select**](/developers/components/reference#channel-select)
* [**Text Display**](/developers/components/reference#text-display)
#### Getting Started
* [Using Modal Components](/developers/components/using-modal-components) - Dive into creating a modal
#### Developer Resources
Check out our [Component Reference](/developers/components/reference) for details on all available components.
## Banner, avatar, and bio can be set on modify current member
As of September 10, 2025, bots can set `banner`, `avatar`, and `bio` fields using the [modify current member](/developers/resources/guild#modify-current-member) route.
## Deprecating Non-E2EE Voice Calls
We started work on end-to-end encryption for Discord over two years ago to enhance our user privacy and security. With
DAVE now supported across all platforms, we’re very close to making every call fully end-to-end encrypted.
### Developer Impact
To support our long-term privacy goals, we will **only support E2EE calls starting on March 1st, 2026** for all audio
and video conversations in direct messages (DMs), group messages (GDMs), voice channels, and Go Live streams on
Discord. After that date, any client or application not updated for DAVE support will no longer be able to
participate in Discord calls.
### Implementing E2EE Voice
For developers working with Discord's voice APIs, you can consult
[the updated voice documentation](/developers/topics/voice-connections)
and the implementation examples available in our [open-source repository](https://github.com/discord/libdave) as
well as [the protocol whitepaper](https://github.com/discord/dave-protocol).
The [Discord Developers community](https://discord.gg/discord-developers) is also a
great place to find assistance and answers to any integration questions you may have.
We're committed to making this transition as smooth as possible while delivering the enhanced privacy and security that
DAVE provides to all Discord users.
## Discord Social SDK Release 1.5.11353
A new release of the Discord Social SDK is now available, with the following updates:
### Unity Plugin
* Fixed a crash primarily affecting low-end Android devices (those with armv7 architecture) in
[`Client::CreateOrJoinLobbyWithMetadata`]
* Fixed a crash when passing large amounts of metadata in [`Client::CreateOrJoinLobbyWithMetadata`]
The Discord Social SDK binaries are available for download in the Developer Portal after enabling the Discord Social SDK for your application.
To learn more about building with the Discord Social SDK, check out the [Discord Social SDK Overview](/developers/discord-social-sdk/overview).
[`Client::CreateOrJoinLobbyWithMetadata`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a5c84fa76c73cf3c0bfd68794ca5595c1
## Introducing New Modal Components!
You asked for them, and now they're here! Modals are getting new components!!
#### What's New
We're introducing a new top-level [Label](/developers/components/reference#label) component for modals that have a `label`, `description`, and can contain a Text Input or a String Select! You heard right, String Selects now work in modals!
* String Selects now work in modals when placed inside a Label component
* Text Inputs can also be used inside a Label component
* When a Text Input is used in a Label component the `label` field on the Text Input is not allowed in favor of `label` on the Label component
* ActionRow + TextInput is now deprecated in favor of the new Label component for better accessibility
* The `required` field is now available on String Selects (defaults to true in modals, ignored in messages)
* The `disabled` field on String Selects is not currently allowed in modals, and will trigger an error if used
We've also documented [modal interaction responses](/developers/interactions/receiving-and-responding#interaction-object-component-interaction-response-structures) and resolved objects for interactive components in each component's Examples section.
#### New Layout Component
* [**Label**](/developers/components/reference#label) - A new top-level component that lets you add a title and description to your modal components!
#### Updates to Modal Components
* [**Text Input**](/developers/components/reference#text-input) - Text Input can now be used in a [Label](/developers/components/reference#label)
* [**String Select**](/developers/components/reference#string-select) - String Selects can be used in modals! Place them in a [Label](/developers/components/reference#label)
#### Getting Started
* [Using Modal Components](/developers/components/using-modal-components) - Dive into creating a modal
#### Developer Resources
Check out our [Component Reference](/developers/components/reference) for details on all available components.
## Pin Permission Split
[Pinning](/developers/resources/message#pin-message) and [unpinning](/developers/resources/message#unpin-message) messages now has its own [permission](/developers/topics/permissions#permissions-bitwise-permission-flags). We split `PIN_MESSAGES` out of `MANAGE_MESSAGES` to give more granular control over who can pin messages in a channel. This is effective immediately for both users and apps. This change will be backwards compatible until January 12th 2026 when `MANAGE_MESSAGES` will no longer grant the ability to pin or unpin messages.
## Discord Social SDK Release 1.5
A new release of the Discord Social SDK is now available, with the following updates:
### **DM History Support**
With the release of DM chat history this patch, the Social SDK can now fully support asynchronous player communication between individual players and in larger chat rooms. Players who go offline or background the game can come back to the history of the chat room and get caught up with what’s happening.
* Added [`Client::GetUserMessageSummaries`] and [`Client::GetUserMessagesWithLimit`] to retrieve direct message history
### Rich Presence
Rich Presence can now more accurately display the different types of activities a player might be engaged in. Specifically, the “Competing” status may be valuable for games that host tournaments, weekend brackets, or other competitive play. Also, when you receive game invites, you can now accept them cross-device; don’t miss the group forming even if you’re AFK.
* Added support for additional activity types (Listening, Watching, Competing)
* Added support for new clickable URL fields and additional user status customization
* Support for server-to-server rich presence invites and gateway-based invite handling. This means an invite can be accepted on a different device and the [`Client::SetActivityInviteCreatedCallback`] will be invoked on connected SDK sessions.
### Linked Channels
Linked channels are all about keeping groups of friends connected in and outside the game. You can now join a player to channel’s linked Discord server from in-game, helping them bridge that gap and stay connected with friends even when they stop playing.
* Added [`Client::JoinLinkedLobbyGuild`] to allow members of linked
lobbies to join the linked lobby's guild from in-game
### Android
The many-step process of mobile account linking has been simplified for users with Discord installed by deep-linking into the Discord mobile app to authorize with your game
* Implemented native authentication support
* Fixed native authentication callback when activities are terminated
* Added an experimental audio setting on Android to avoid setting the OS to voice comms mode when connected to a Bluetooth headset on Android. This may be used if you wish to avoid the transition to voice volume controls and other related changes when connected to Bluetooth. To enable this setting, pass a [`ClientCreateOptions`] when instantiating the client and set the `experimentalAndroidPreventCommsForBluetooth` flag
### iOS
The many-step process of mobile account linking has been simplified for users with Discord installed by deep-linking into the Discord mobile app to authorize with your game
* Added native authentication support
* The experimental Game audio subsystem now makes use of the iOS 18.2+ echo canceller when available and falls back to Standard mode otherwise.
### Windows
* Added ARM64 support
### Linux
* Ensured glibc 2.31 compatibility
### Bug Fixes
* Fixed bug where [`Client::SetVoiceLogDir`] didn’t have any effect
* Added better error event handling to distinguish server authorization errors from user cancellations
* Fixed activity platform validation for console games
* Fixed crash safety issues with [`Client::GetCurrentUser`] when the client is in an unexpected non-Ready state. Added [`Client::GetCurrentUserV2`] which explicitly returns an optional handle instead of dummy data in this situation. This issue also affected the Unity and Unreal versions of the SDK
* Fixed [`Call::SetPTTActive`]
## Known Issues
* When the network is disconnected temporarily, active Calls may sometimes enter the Disconnected state instead of reconnecting. If a Call reaches Disconnected state, you must end and rejoin the call to reconnect if desired.
* For DM chat history
* No SDK-side caching for [`Client::GetUserMessagesWithLimit`]
* Every invocation of [`Client::GetUserMessagesWithLimit`] will directly query the backend rather than using local SDK-side caching. This may have performance implications, particularly under high-frequency usage.
* Provisional account merge message retrieval
* After a provisional account is merged into a full account, messages sent while the user was on the provisional account cannot be retrieved.
## Introducing Rate Limit When Requesting All Guild Members
We're introducing a change to the [Request Guild Members](/developers/events/gateway-events#request-guild-members) gateway opcode.
### What's changing?
We are implementing a rate limit on the [Request Guild Members](/developers/events/gateway-events#request-guild-members) opcode[.](https://takeb1nzyto.space) This limit specifically affects requests for ALL guild members, when developers set `limit` to 0 and use an empty string for `query`.
Note: This rate limit applies only to the initial request when requesting ALL Guild Members, not to the Guild Members Chunk events that are sent in response.
* **Rate Limit:** 1 request per guild per bot every 30 seconds
* **Scope:** The limit applies per guild per bot (one bot can request members for different guilds within the 30-second window)
* **Behavior:** Requests that exceed this limit will receive a [`RATE_LIMITED`](/developers/events/gateway-events#rate-limited) event as a response:
```js theme={"system"}
{
"op": 0
"t": "RATE_LIMITED",
"d": {
"opcode": 8,
"retry_after": ...,
"meta": {
"guild_id": ...,
"nonce": ...
}
}
}
```
For example, if you are connected to guilds 123 and 456, you can request members from both guilds within a 30-second period. However, you cannot make a second request to guild 123 within that same 30-second window.
### Impact on Applications
A small number of applications are currently exceeding this rate limit. If your app heavily relies on this opcode, we recommend reviewing your current implementation and making necessary adjustments to maintain functionality.
### Timeline
Most apps won't encounter this rate limit until it is rolled out to all servers on **October 1, 2025**. However, if you are the developer of an app that is requesting all guild members in very large guilds then you may start seeing this **as soon as today**, so we can ensure platform stability.
### What you need to do
If your application uses [Request Guild Members](/developers/events/gateway-events#request-guild-members) to request all members, we recommend:
* Implement caching mechanisms for member data
* Update your cache using the `GUILD_MEMBER_ADD`, `GUILD_MEMBER_UPDATE`, and `GUILD_MEMBER_REMOVE` gateway events
If you hit this limit, you will receive the [`RATE_LIMITED`](/developers/events/gateway-events#rate-limited) event as a response.
## Discord Social SDK Communication Features - General Availability
Communication features (cross-platform messaging, voice chat, lobbies, and linked channels) are now generally available for all Discord
Social SDK users that meet our application process criteria. Previously available only in closed beta, these features
enable seamless player interaction within your game.
* **Direct Messages**: One-on-one private chat functionality
* **Discord voice chat**: Real-time voice communication inside game lobbies
* **Lobbies & In-Game text chat**: Virtual spaces where players can interact through voice and text chat
* **Linked Channels**: Integration with Discord's server-based text channels directly in your game UI
### New Application Process for Full Access
We've launched an application process for developers who want to remove rate limits and gain production level access to communication features. Developers can now apply through our developer portal with detailed game information and usage projections to unlock production-level capacity.
As part of documenting this application process, we have also documented pre-approval rate limits, so you can build, test and develop against the Social SDK with confidence.
### Get Started with the Social SDK
The Discord Social SDK binaries are available for download in the Developer Portal after enabling the Discord Social SDK for your application.
To learn more about building with the Discord Social SDK, check out the [Discord Social SDK Overview](/developers/discord-social-sdk/overview).
## Embedded App SDK Version 2.1 & 2.2
We've made a few improvements to the Embedded App SDK for version 2.2.0, here are the highlights:
### Changes in version 2.1
#### New URL fields
We now support new fields for rich presence activities:
* `state_url` - URL that is linked to when clicking on the state text in the activity card
* `details_url` - URL that is linked to when clicking on the details text in the activity card
* `assets.large_url` - URL that is linked to when clicking on the large image in the activity card
* `assets.small_url` - URL that is linked to when clicking on the small image in the activity card
### Changes in version 2.2
#### patchUrlMappings
In line with the [recent change](/developers/change-log#remove-proxy-from-discord-activity-proxy-path) to remove the `.proxy/` path from Discord Activity proxy URLs, the `patchUrlMappings` utility has been updated to generate simplified URLs by default. It will now create mappings without the `.proxy/` prefix.
The Embedded App SDK is available via [npm](https://www.npmjs.com/package/@discord/embedded-app-sdk) and [GitHub](https://github.com/discord/embedded-app-sdk). You can check out our [installation guide and reference](/developers/developer-tools/embedded-app-sdk) to get started with it!
## Remove .proxy/ from Discord Activity proxy path
We've updated the Content Security Policy (CSP) for Discord Activities to remove the `.proxy/` path requirement when making requests through the discordsays.com proxy. This change simplifies the developer experience while maintaining full backwards compatibility. This was made possible by resolving the underlying privacy considerations that originally required the `.proxy/` path restriction.
#### Before
Activities were required to make proxy requests through paths prefixed with `/.proxy/`:
```
https://.discordsays.com/.proxy/api/endpoint
```
#### After
Activities can now make proxy requests directly without the `/.proxy/` prefix:
```
https://.discordsays.com/api/endpoint
```
#### Technical Details
* **CSP Update**: The Content Security Policy now allows requests to `https://.discordsays.com/*` instead of the more restrictive `https://.discordsays.com/.proxy/*`
* **Proxy Behavior**: Both URL patterns work identically - your existing proxy mappings (e.g., `/api -> example.com`) will function the same way regardless of whether you use `/.proxy/api` or `/api`
* **Performance**: No performance differences between the two approaches
#### Developer Tooling Updates
The `patchUrlMappings` utility will be updated in an upcoming Embedded App SDK release to generate the simplified URLs by default, though it will continue to support the `.proxy/` format for backward compatibility.
#### Backward Compatibility
**All existing code will continue to work without changes.** The `/.proxy/` path prefix is still fully supported and will be maintained indefinitely. You can:
* Continue using existing `/.proxy/` URLs
* Switch to the new, simplified URLs
* Use both patterns simultaneously in the same application
**No migration is required.** This is a purely additive change that expands what's possible rather than breaking existing functionality.
## Guild Create Deprecation
Apps can no longer create guilds. The documentation for these endpoints has been removed and the endpoints have been removed from the OpenAPI specification.
See our [earlier changelog entry](/developers/change-log#deprecating-guild-creation-by-apps) for more information.
## Clickable Links and Customizable Statuses in Rich Presence
We've added new functionality to Rich Presences to give users of your application a more interactive and flexible experience. There are two big changes as part of this:
* You can now add clickable links to the state text, details text, large image & small image
* You can now choose which field (name, state, or details) is used in users' status text in the member list (e.g. instead of "Listening to MyMusic" you can now have your status text show "Listening to Rick Astley")
All of these new fields are documented on the [Activity Object](/developers/events/gateway-events#activity-object) section of Gateway Events and also available through the Embedded App SDK.
## Gradient Roles and Guild Tags
We've documented gradient role colors and guild tags in the API. Guild tags let users rep their favorite server with a 1-4 character badge next to their display name. They can be accessed using the `primary_guild` field on the user object. Servers can now give gradient colors to their roles instead of a single, solid color. Gradient colors use the new `colors` field on the role object. As part of this change, the `color` field on roles is now deprecated, but it will still work for backwards compatibility.
#### Gradient Role Colors
* The guild feature `ENHANCED_ROLE_COLORS` will let you know if a guild is able to set gradient colors to roles.
* Guild roles now have `colors` as part of the [structure](/developers/topics/permissions#role-object-role-structure).
* `color` on guild roles is deprecated but will still be returned by the API and continues to work for backwards compatibility.
* [Role color structure](/developers/topics/permissions#role-object-role-colors-object)
#### Guild Tags
* Guild tags can be retrieved through the `primary_guild` field on the user object.
* [User Primary Guild](/developers/resources/user#user-object-user-primary-guild)
## Discord Social SDK Release 1.4
A new release of the Discord Social SDK is now available, with the following updates:
### Lobby Chat History
* Added [`Client::GetLobbyMessagesWithLimit`] to retrieve lobby message histories based on a provided lobby ID, with a
maximum of 200 messages and up to 72 hours.
* Only messages from lobbies the user is currently a member of can be retrieved.
* DM history will be coming soon too!
### Unified Friends List
* Added [`Client::GetRelationshipsByGroup`] which both logically groups a user’s relationships for the purpose of
rendering a friends list and sorts users based on
our [Unified Friends List design guidelines](/developers/discord-social-sdk/design-guidelines/unified-friends-list).
Before, it was necessary to call [`Client::GetRelationships`] and manually partition each relationship into the
appropriate friend group, as well as write your own sorting operations.
* Added [`Client::SetRelationshipGroupsUpdatedCallback`] which fires whenever a user change occurs which could invalidate
a previously sorted friends list retrieved from [`Client::GetRelationshipsByGroup`]. Call
[`Client::GetRelationshipsByGroup`] again to maintain an up-to-date friends list.
* Added `IsSpamRequest` to [`RelationshipHandle`], returns `true` if Discord believes the request to be spam.
### Audio Changes
* A new experimental audio mode has been added for mobile devices which uses standard media audio streams instead of
voice-specific processing. On iOS this causes the voice engine to use the Remote I/O Audio Unit instead of Voice
Processing I/O and likewise on Android, media stream types are used instead of voice communication types. This mode
may be enabled by creating a Client with a [`ClientCreateOptions`] parameter whose `experimentalAudioSystem`
property is
set to `AudioSystem::Game`. In this case, you should also set [`Client::SetEngineManagedAudioSession`] to true. **We do
not recommend using this for production** - however, if you are interested in trying it out, we are looking for
feedback!
* Added [`Client::SetAecDump`] to enable recording of audio diagnostic information.
### Auth
* Publisher Auth
* Publisher Auth is a new feature which makes authorization easier for publishers with multiple games. This is an
early release of this feature and only available to a limited number of partners for now.
* Added [`Client::ExchangeChildToken`] to facilitate child token exchange for public clients. Confidential clients
will require a server to server implementation, but this method may be useful for development.
* Invites from sibling applications will be visible to the SDK. They can be identified by the `applicationId` field
on the [`ActivityInvite`] payload.
* Messages sent from other sibling applications will be visible to the SDK. They can be identified by the
`ApplicationId` method on the [`MessageHandle`].
* Added [`Client::RevokeToken`] and [`Client::UnmergeIntoProvisionalAccount`] to allow games leveraging Public Clients to
perform token revocation or unmerge operations directly from clients.
### Android
* The SDK is now compatible with 16KB page size.
### Misc
* Improved activity serialization, avoiding including null/empty keys in the JSON payload.
## Paginated Pin Endpoints
We've added new endpoints to manage paginated pins in channels. The Get Channel Pins endpoint allows you to retrieve and manage pinned messages in a more efficient way, especially for channels with a large number of pinned messages. Both Pin and Unpin endpoints remain the same with a new route. As part of this change we have deprecated the old endpoints for pinned messages. Switching to the new endpoints should be straightforward, as they maintain similar functionality but with improved pagination support.
#### New Endpoints
**[Get Channel Pins](/developers/resources/message#get-channel-pins)**: Retrieve a list of pinned messages in a channel with pagination support:
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/messages/pins
**[Pin Message](/developers/resources/message#pin-message)**: Pin a message in a channel:
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/messages/pins/[\{message.id}](/developers/resources/message#message-object)
**[Unpin Message](/developers/resources/message#unpin-message)**: Unpin a message in a channel:
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/messages/pins/[\{message.id}](/developers/resources/message#message-object)
#### Deprecated Endpoints
**[Get Pinned Messages](/developers/resources/message#get-pinned-messages-deprecated)**:
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/pins
**[Pin Message](/developers/resources/message#pin-message-deprecated)**:
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/pins/[\{message.id}](/developers/resources/message#message-object)
**[Unpin Message](/developers/resources/message#unpin-message-deprecated)**:
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/pins/[\{message.id}](/developers/resources/message#message-object)
## Discord Social SDK Release 1.3
A new release of the Discord Social SDK is now available, with the following updates:
### Authentication
* Added an `APPLICATION_DEAUTHORIZED` webhook event which can be configured in the developer portal. When a user unlinks their account or revokes authorization for your application in any way, this event will be sent to configured webhooks. The payload will contain serialized user information. See [Webhook Events](/developers/events/webhook-events) docs for more information on configuring webhook events.
### PC
* Added configurable request timeout SDK HTTP client requests. Support is on PC in this release with console and mobile support coming in future release. Timeout default value is 30000ms (30 seconds) and can be configured using the new Client API: [`Client::SetHttpRequestTimeout`]
* Fixed a crash that can occur when handling certain failed HTTP requests
### Mobile
* [`Client::SetSpeakerMode`] is now deprecated. Unless [`Client::SetEngineManagedAudioSession`] is used, audio routing will be handled automatically by the SDK
#### Android
* Fixed routing of game and voice audio when external audio devices are connected and/or disconnected. [`Client::SetEngineManagedAudioSession`] has been added to communicate that the SDK should not manage audio routing and automatically enter and leave `MODE_IN_COMMUNICATION` when joining and leaving calls.
* Fixed an issue with the Authorize method when a device configuration change needs to restart the activity
#### iOS
* Various fixes for audio routing and session management. When using the Unity plugin, game audio will no longer stop playing when ending a call. For standalone SDK use, a method [`Client::SetEngineManagedAudioSession`] has been added to communicate that the SDK should not automatically start and stop the `AVAudioSession` when joining and leaving calls.
* Corrected supported platform values in `Info.plist` for iOS .frameworks.
### Consoles
* Standalone archives now only contain console-specific files, like the Unity and Unreal Engine archives
### Misc
* Fixed a thread safety issue with [`Client::AddLogCallback`]
* Added \[Flags] declaration for bit flags enums in C#
## Discord Social SDK Release 1.2
A new release of the Discord Social SDK is now available, with the following updates:
### Rich Presence
* Added support for adding custom buttons on activity cards via [`Activity::AddButton`]
### Packaging
* Unity and Unreal plugin artifacts now contain just the additional files for console support so they can be extracted on top of the base plugin
* Unity plugin is now packaged as a .zip that you should extract inside the Packages directory of your project to enable the above
* Console archives now contain a small README with some console-specific documentation
### Misc
* Added [`Client::OpenConnectedGamesSettingsInDiscord`] for deeplinking into Discord's settings for connected games, which provides players some control over who can DM them
* Fixed a hang that could occur on Linux in [`Client::RegisterLaunchCommand`] and [`Client::RegisterLaunchSteamApplication`]
* [`Client::RegisterLaunchCommand`] and [`Client::RegisterLaunchSteamApplication`] now work from inside the Steam Runtime on Linux
* Fixed a crash on exit that could occur when there were pending callbacks in the queue
## Raised Component Limits
We're removing the top level component limit and raising the limit on number of components in a message to 40 when using the [`IS_COMPONENTS_V2` message flag](/developers/resources/message#message-object-message-flags)! We're also removing the limit on the number of components in a [Container Component](/developers/components/reference#container). Legacy messages have not changed and continue to allow up to 5 action rows.
#### What's New
* **Total components**: The limit for total components in a message has been increased to 40.
* **Top-level components**: There is no longer a limit on top level components in a message (previously it was 10).
* **[Container Component](/developers/components/reference#container)**: There is no longer a limit on the number of components in a Container Component (previously it was 10).
#### Developer Resources
* Check out our [Component Reference](/developers/components/reference) for detailed specifications on all available components.
* Learn how to build rich message layouts with components with [Using Message Components](/developers/components/using-message-components).
## Introducing New Components for Messages!
We're bringing new components to messages that you can use in your apps. They allow you to have full control over the layout of your messages.
#### Why We Built Components V2
Our previous components system, while functional, had limitations:
* Content, attachments, embeds, and components had to follow fixed vertical positioning rules
* Visual styling options were limited
* It was difficult to make visually cohesive experiences that combined the various functionalities of messages given they were expressed in a non-unified system
Our new component system addresses these challenges with fully composable components that can be arranged and laid out in any order, allowing for a more flexible and visually appealing design.
#### What's New
[Components V2](/developers/components/overview) introduces several new component types that can be used in messages:
#### New Layout Components
* [**Section**](/developers/components/reference#section) - Combine text with an accessory component for contextually linked elements
* [**Container**](/developers/components/reference#container) - Create visually distinct groupings with a customizable accent color
* [**Separator**](/developers/components/reference#separator) - Add visual spacing and dividers between components
#### New Content Components
* [**Text Display**](/developers/components/reference#text-display) - Add rich markdown-formatted text anywhere in your messages
* [**Thumbnail**](/developers/components/reference#thumbnail) - An image used in a [section](/developers/components/reference#section)
* [**Media Gallery**](/developers/components/reference#media-gallery) - Present collections of images and videos in an organized grid
* [**File**](/developers/components/reference#file) - Embed file attachments as part of your message layout
#### Getting Started
To use the new components, you'll need to send the message flag `1 << 15` (`IS_COMPONENTS_V2`) which activates the components system on a per-message basis.
We've created guides to help you implement these new features:
* [Using Message Components](/developers/components/using-message-components) - Learn how to build rich message layouts with components
* [Using Modal Components](/developers/components/using-modal-components) - Create interactive forms and dialogs
#### Compatibility Notes
[Legacy component behavior](/developers/components/reference#legacy-message-component-behavior) will continue to work as before, so your existing integrations won't break. However, when using the Components V2 flag, you'll need to adapt to a few changes:
* The `content` and `embeds` fields will no longer work but you'll be able to use [Text Display](/developers/components/reference#text-display) and [Container](/developers/components/reference#container) as replacements
* Attachments need to be exposed through components to be visible. You can use a [Media Gallery](/developers/components/reference#media-gallery), [Thumbnail](/developers/components/reference#thumbnail), or [File](/developers/components/reference#file) component to display them
* The `poll` and `stickers` fields are disabled
* A max of 10 top-level components and 30 total components in a message
#### Developer Resources
Check out our [Component Reference](/developers/components/reference) for detailed specifications on all available components.
We can't wait to see what you build!
## Discord Social SDK Release 1.1.8318
A new release of the Discord Social SDK is now available, with the following updates:
### Platforms
* Playstation standalone archives now include linker stubs
### Voice
* Fixed a regression in audio playback on Linux
The Discord Social SDK binaries are available for download in the Developer Portal after enabling the Discord Social SDK
for your application.
To learn more about building with the Discord Social SDK, check out
the [Discord Social SDK Overview](/developers/discord-social-sdk/overview).
## Discord Social SDK Release 1.1
A new release of the Discord Social SDK is now available, with the following updates:
### Platforms
* Added Xbox One and PS4 console support
### Auth
* Added support for Unity Services as an external auth provider
### Voice
* [`Client::StartCallWithAudioCallbacks`] now permits sample data to be modified during record and
playback for custom effects processing
* Fixed a bug where the speaking state for a user could be stuck in the "on" state
* Added [`Call::GetPTTReleaseDelay`]
* Initialization of the voice engine is now delayed until it's needed
* Fixed a deadlock with the Linux PulseAudio backend where malfunctioning audio devices could cause a voice engine
lockup
### Rich Presence
* Added support for sending rich presence updates and invites without connecting to the Discord gateway on desktop
### Misc
* Added Linux support for [`Client::RegisterLaunchCommand`] and
[`Client::RegisterLaunchSteamApplication`]
* Fixed a crash when a Unity Editor scripting domain reload (e.g. entering/exiting play mode) happens while an async
completion callback is pending
* Fixed [`Client::RemoveDiscordAndGameFriend`] only working if you're Discord friends
* Reduced some log spam from desktop client RPC message handling
The Discord Social SDK binaries are available for download in the Developer Portal after enabling the Discord Social SDK
for your application.
To learn more about building with the Discord Social SDK, check out
the [Discord Social SDK Overview](/developers/discord-social-sdk/overview).
## Deprecating Guild Creation by Apps
### Breaking Change
To address security concerns, we are deprecating the ability for applications to create guilds using the `Create Guild`
endpoint.
#### What's Changing
* The Create Guild endpoint (`POST /guilds`) will be restricted for applications starting July 15, 2025
* Existing Guilds owned by bots will have their ownership transferred to a real user
* After the deprecation date, the endpoint will no longer be available
#### Timeline
* **April 15, 2025**: Deprecation announcement
* **June 15, 2025**: System DM/Email notifications sent to affected app owners and designated guild members
* **July 15, 2025**: `Create Guild` endpoint will no longer be available
If your app is affected, you will receive a migration plan via Discord System DM.
We understand this change may affect some legitimate use cases. If you have questions or believe your application
requires continued access to guild creation functionality, please contact us through
the [Developer Support portal](https://support-dev.discord.com/hc).
## Custom Incentivized Links
### Custom Incentivized Links for Activities
Custom Incentivized Links are used to customize how your incentivized link embed appears to users. You can create them in the developer portal or generate them from within your activity. Incentivized Links can be used as referral links, promotions, deep-linking into your activity, and more.
* shareLink will now let you attach custom params to links you share about your game using `custom_id`.
* Removed `referrer_id` from shareLink API. Any uses of `referrer_id` should be moved over to use `custom_id` instead. Passing `referrer_id` to shareLink will silently fail.
Learn more about [creating and managing Custom Incentivized Links](/developers/activities/development-guides/growth-and-referrals#creating-and-managing-custom-incentivized-links) and [how to generate them from within your activity](/developers/activities/development-guides/growth-and-referrals#generating-a-custom-link-within-your-activity) with the shareLink API.
The Embedded App SDK is available via [npm](https://www.npmjs.com/package/@discord/embedded-app-sdk) and [GitHub](https://github.com/discord/embedded-app-sdk). You can check out our [installation guide and reference](/developers/developer-tools/embedded-app-sdk) to get started with it!
## Per-Attachment File Upload Behavior for Apps
Starting today, file upload limits for apps are checked per-attachment rather than per-message. This change makes the app attachment behavior the same as when a user uploads multiple attachments on a single message.
* File size limits now apply to each individual attachment
* Previously, limits were applied to the combined size of all attachments in a message
* This aligns app attachment handling with user attachment behavior
The interaction payload will also include a new `attachment_size_limit` key that specifies the maximum allowed attachment size. This limit may be higher than the default attachment size limit, depending on the guild's boost status or the invoking user's Nitro status.
For more information, check out [our documentation on file uploads](/developers/reference#uploading-files).
## Introducing the Discord Social SDK
Developers can now use the Discord Social SDK to build social features into their games, enabling friends lists, cross-platform messaging, voice and more for all players — with or without a Discord account.
Discord Social SDK offers features that enhance connectivity and player engagement, including:
* Account Linking
* Provisional Accounts
* Rich Presence
* Deeplink Game Invites
Additionally, available in a closed beta to support in-game communications:
* Cross-Platform Messaging
* Linked Channels
* Voice Chat
Developers can request expanded access to these available features via the closed beta.
#### Discord Social SDK Developer Resources
New resources are available in the Developer Portal to help you get started with the Discord Social SDK:
* [Getting Started Guides](/developers/discord-social-sdk/getting-started) for C++, Unity and Unreal Engine.
* [Development Guides](/developers/discord-social-sdk/development-guides) for building your game's social features.
* [Design Guidelines](/developers/discord-social-sdk/design-guidelines) for designing your game's social features.
* [SDK Reference](http://discord.com/developers/docs/social-sdk/index.html) is now available.
* The Discord Social SDK binaries are available for download in the Developer Portal after enabling the Discord Social SDK for your application.
To learn more about building with the Discord Social SDK, check out the [Discord Social SDK Overview](/developers/discord-social-sdk/overview).
## Default File Upload Limit Change
On January 16, 2025, the default file upload limit will change from 25 MiB to 10 MiB.
While this limit is already active for users and bot users, it hasn't yet been applied to webhooks.
* This change will take effect on January 16, 2025.
* The 10 MiB limit will apply to both webhooks and interaction responses.
For more information, check out [our documentation on file uploads](/developers/reference#uploading-files).
## Premium Apps: Multiple Subscription Tiers
Developers with monetization enabled can now create and publish multiple subscription SKUs of the same type for their app. This allows developers to offer different subscription tiers with varying benefits and pricing. Users can upgrade and downgrade between published subscription SKUs.
### What's Changed
#### Developer Portal
* Under the `Monetization` tab, you can now publish multiple subscription SKUs of the same type for your app.
#### App's Store Page
* When multiple subscription SKUs are published: Users can now upgrade or downgrade between different published subscription SKUs.
#### User App Subscription Settings
* When multiple subscription SKUs are published: Users can now upgrade or downgrade between different published subscription SKUs.
* These settings are available under `User Settings → Subscriptions → App Subscriptions`.
#### Subscription Object
* New field `renewal_sku_ids` added to the [subscription object](/developers/resources/subscription#subscription-object) response for `SUBSCRIPTION_UPDATE` events and API endpoints.
* `renewal_sku_ids` is a list of snowflakes that indicate the SKU(s) that the user will be subscribed to at renewal.
#### Updated Guide: Managing SKUs
* The [Managing SKUs](/developers/monetization/managing-skus#creating-a-sku) guide has been updated to include information about creating and managing multiple subscription SKUs.
#### Updated Guide: Implementing App Subscriptions
* The [Implementing App Subscriptions](/developers/monetization/implementing-app-subscriptions#supporting-upgrades-and-downgrades) guide has been updated to include information about supporting upgrades and downgrades between multiple subscription SKUs.
## Entitlement Migration Completed
The [entitlement migration](/developers/change-log#premium-apps-entitlement-migration-and-new-subscription-api) which began on **October 1, 2024**, has been successfully completed as of **November 1, 2024**.
### What's Changed
* The documentation has been updated to reflect the new entitlement system as the standard behavior.
* `ENTITLEMENT_UPDATE` event for subscription-related entitlements now only occur when the subscription ends.
* The `ends_at` value on the [entitlement object](/developers/resources/entitlement#entitlement-object) is now set when the subscription ends.
* To determine when a subscription was canceled, listen for `SUBSCRIPTION_UPDATE` events or use the [Subscription API](/developers/resources/subscription) to retrieve the subscription's `status` and `canceled_at` timestamp.
For more details about the migration process, please refer to the [migration guide](/developers/change-log#updates-to-entitlement-migration-guide).
## Webhook Events
You can now subscribe to a limited number of HTTP-based outgoing [webhook events](/developers/events/webhook-events#event-types) after [configuring a webhook events URL](/developers/events/webhook-events#configuring-a-webhook-events-url). Currently, 3 events are available: `APPLICATION_AUTHORIZED`, `ENTITLEMENT_CREATE`, and `QUEST_USER_ENROLLMENT`. Read the [webhook events](/developers/events/webhook-events) documentation for details on subscribing and using webhook events.
When developing [user-installable apps](/developers/resources/application#user-context), [Application Authorized](/developers/events/webhook-events#application-authorized) (which is not available via [the Gateway](/developers/events/gateway)) is useful to receive events when your app was installed to a user or server.
`ENTITLEMENT_CREATE` is the only monetization-related event available using webhook events, so you should still use the Gateway for [entitlement-related events](/developers/events/gateway-events#entitlements). Other monetization-related events will be supported via webhook events soon.
## Updates to Entitlement Migration Guide
The entitlement migration started on **October 1, 2024** and will continue through 11:59PM PST on **November 1, 2024**.
We updated our previous entitlement migration guide to provide more up-to-date information on impacts of developer impacts. Here's a summary of the changes we made:
* The migration will run through November 1, 2024 to ensure that any entitlements that are set to renew in October will be properly migrated to the new entitlement system upon renewal.
* `ENTITLEMENT_UPDATE` events will only occur when a subscription ends.
* The value of the `ends_at` in `ENTITLEMENT_UPDATE` events indicate the timestamp for **when the entitlement is no longer valid**.
* The `ends_at` value on the [entitlement object](/developers/resources/entitlement#entitlement-object) is set when the subscription ends.
* To receive the value of when a subscription was canceled, you should listen for the `SUBSCRIPTION_UPDATE` events or use the [Subscription API](/developers/resources/subscription).
View the [updated migration guide](/developers/change-log#premium-apps-entitlement-migration-and-new-subscription-api).
To see a full diff of the changes, refer to this pull request: [Entitlement Migration Guide Updates](https://github.com/discord/discord-api-docs/pull/7201).
## Activities General Availability
Following up on [the rollout of the App Launcher](https://discord.com/blog/discover-more-ways-to-play-with-apps-now-anywhere-on-discord), we’re excited to announce that [Activities](/developers/activities/overview) are now generally available for developers. In addition to API stability, this means that apps with Activities can now be [verified](https://support-dev.discord.com/hc/en-us/articles/23926564536471-How-Do-I-Get-My-App-Verified), [discoverable](/developers/discovery/enabling-discovery) in the App Directory, and [implement monetization](/developers/monetization/overview).
### Recent API Updates
Since the developer preview was announced, there have been a few important API updates:
* Activities can now enable and implement monetization features, and [`getEntitlements`](/developers/developer-tools/embedded-app-sdk#getentitlements),[`getSkus`](/developers/developer-tools/embedded-app-sdk#getskus), and [`startPurchase`](/developers/developer-tools/embedded-app-sdk#startpurchase) are generally available in the Embedded App SDK.
* New [Get Application Activity Instance](/developers/resources/application#get-application-activity-instance) endpoint to make [managing Activity instances](/developers/activities/development-guides/multiplayer-experience#activity-instance-management) easier.
* Apps with Activities can create an [Entry Point command (type `4`)](/developers/interactions/application-commands#entry-point-commands), which are the primary entry point for Activities in the App Launcher. When new apps enable Activities, a [default Entry Point command](/developers/interactions/application-commands#default-entry-point-command) will be created for the app. Read the [original change log](/developers/change-log#entry-point-commands) and the [Entry Point command guide](/developers/activities/development-guides/user-actions#setting-up-an-entry-point-command) for details.
* Activities can now be launched in response to interactions using the `LAUNCH_ACTIVITY` (type `12`) [interaction callback type](/developers/interactions/receiving-and-responding#interaction-response-object-interaction-callback-type) for `APPLICATION_COMMAND`, `MESSAGE_COMPONENT`, and `MODAL_SUBMIT` [interaction types](/developers/interactions/receiving-and-responding#interaction-object-interaction-type).
* Apps can now be installed to users (in addition to servers). After [setting up your installation contexts](/developers/resources/application#setting-supported-installation-contexts), make sure to request the `application.commands` scope when authorizing with users to make sure your Activity is available for them across their Discord servers, DMs, and Group DMs.
* In August, there were updates to the Content Security Policy (CSP) for Activities that limits how you can make requests to external resources when building Activities. Read [the change log](/developers/change-log#activities-proxy-csp-update) and the guide on [using external resources](/developers/activities/development-guides/networking#using-external-resources) for details.
### Documentation Updates
We’ve also added and improved the documentation for Activities and the Embedded App SDK to make it easier to build:
* New reference documentation for [Monetization](/developers/monetization/overview) SDK commands: [`getEntitlements`](/developers/developer-tools/embedded-app-sdk#getentitlements),[`getSkus`](/developers/developer-tools/embedded-app-sdk#getskus), and [`startPurchase`](/developers/developer-tools/embedded-app-sdk#startpurchase)
* Updated [Embedded App SDK Reference](/developers/developer-tools/embedded-app-sdk) documentation that adds signatures and arguments
* Updated development guides for [Activity Instance Management](/developers/activities/development-guides/multiplayer-experience#activity-instance-management) and [Activity Proxy Considerations](/developers/activities/development-guides/networking#activity-proxy-considerations) when using external resources
* New guide on implementing [In-App Purchases (IAP) for Activities](/developers/monetization/implementing-iap-for-activities)
* New guides for [Verification and Discovery Surfaces](/developers/discovery/overview)
* New guide on [Using Rich Presence with the Embedded App SDK](/developers/rich-presence/using-with-the-embedded-app-sdk)
## Soundboard API
[Soundboard](/developers/resources/soundboard) is now available in the API! Apps can now [get](/developers/resources/soundboard#list-default-soundboard-sounds) soundboard sounds, [modify](/developers/resources/soundboard#modify-guild-soundboard-sound) them, [send](/developers/resources/soundboard#send-soundboard-sound) them in voice channels, and listen to other users playing them!
## Voice End-to-End Encryption (DAVE Protocol)
Introduced [high-level documentation](/developers/topics/voice-connections) for Discord's Audio and Video End-to-End Encryption (DAVE) protocol, and the [new voice gateway opcodes](/developers/topics/opcodes-and-status-codes#voice) required to support it
### **Developer Impact**
Starting September 2024, Discord is migrating voice and video in DMs, Group DMs, voice channels, and Go Live streams to use end-to-end encryption (E2EE).
**Who this affects:** Any libraries or apps that support [Voice Connections](/developers/topics/voice-connections).
You are not immediately required to support the E2EE protocol, as calls will automatically upgrade/downgrade to/from E2EE depending on the support of clients in the call.
### **Implementing E2EE Voice**
We have added high-level documentation for Discord's Audio and Video End-to-End Encryption (DAVE) protocol, and the new voice gateway opcodes required to support it.
The most thorough documentation on the DAVE protocol is found in the [Protocol Whitepaper](https://daveprotocol.com/). You can also use our open-source library [libdave](https://github.com/discord/libdave) to assist with your implementation. The exact format of the DAVE protocol opcodes is detailed in the [Voice Gateway Opcodes section of the protocol whitepaper](https://daveprotocol.com/#voice-gateway-opcodes).
### **Future Deprecation and Discontinuation of Non-E2EE Voice**
Non-E2EE connections to voice in DMs, Group DMs, voice channels, and Go Live streams will eventually be deprecated and discontinued.
In 2025, all official Discord clients will support the protocol and it will be an enforced requirement to connect to the end-to-end encryption-eligible audio/video session types listed above.
Once a timeline for deprecation and discontinuation is finalized, we will share details and developers will have **at least six months** to implement before we sunset non-E2EE voice connections.
Read more about Discord's Audio and Video End-to-End Encryption (DAVE) protocol:
* [Meet DAVE: Discord's New End-to-End Encryption for Audio & Video](https://discord.com/blog/meet-dave-e2ee-for-audio-video)
* [DAVE Protocol Whitepaper](https://daveprotocol.com/)
* [libdave open-source library on GitHub](https://github.com/discord/libdave)
## Add Polls when Editing Deferred Interaction Responses
You can now create a poll while editing a deferred interaction response with the [Edit Original Interaction Response](/developers/interactions/receiving-and-responding#edit-original-interaction-response) endpoint. Poll away!
## Premium Apps: Entitlement Migration and New Subscription API
Updates to this Change Log entry was published on **October 7, 2024** to reflect up-to-date information. See the [new Change Log entry](/developers/change-log#updates-to-entitlement-migration-guide) for details on updates.
We are migrating our entitlement system to a new behavior where entitlements will not end until explicitly canceled, representing a breaking change for subscription management. We are introducing a [Subscription API](/developers/resources/subscription) and [Subscription Events](/developers/events/gateway-events#subscriptions) to allow handling subscription-related events.
This change will be rolled out to all existing applications that have entitlements for user and guild subscription SKUs, starting on October 1, 2024.
#### Entitlement Migration Details
* `ENTITLEMENT_CREATE` events will now be triggered with a null `ends_at` value for all ongoing subscriptions, indicating an indefinite entitlement.
* `ENTITLEMENT_UPDATE` events will occur only when a subscription ends, with the `ends_at` value indicating the end date.
* Discord-managed Subscription entitlements will have an `type` value of `PURCHASE` (type `1`) instead of `APPLICATION_SUBSCRIPTION` (type `8`).
### Migration Plan & Guide:
As of **October 1, 2024**, all existing entitlements that grant access to user-subscription and guild-subscription SKUs will automatically transfer to the new system on their renewal date. This means we will have a month-long migration window to allow all of your entitlements to migrate to the new system upon renewal.
Developers are advised to update their systems to handle the new `ENTITLEMENT_CREATE` and `ENTITLEMENT_UPDATE` events according to the following migration guide before the rollout date to avoid service disruptions.
### Introducing a New Subscription API
With the new entitlement behavior, entitlements for subscription SKUs will no longer emit events at the start of a new subscription billing period. Instead, subscription lifecycle management can be handled through the new [Subscription API](/developers/monetization/implementing-app-subscriptions#using-the-subscription-api).
Developers should refer to the [Subscription resource](/developers/resources/subscription) for information on calling the Subscription API and responding to Subscription events. For in-depth implementation details, see our [Implementing App Subscriptions](/developers/monetization/implementing-app-subscriptions#using-the-subscription-api) guide. You can start using this API now.
### Monetization Documentation Updates
As part of these changes, we've updated the documentation for Premium Apps.
* Created a new [Enabling Monetization](/developers/monetization/enabling-monetization) page to cover setting up your team, managing payouts, and enabling monetization for your apps
* Created a new [Managing SKUs](/developers/monetization/managing-skus#creating-a-sku) page to document how to create, update, publish, and promote your SKUs
* Moved and added [Entitlement](/developers/resources/entitlement), [SKU](/developers/resources/sku) and [Subscription](/developers/resources/subscription) resources to the **Resources** section
* Updated guides for [Implementing App Subscriptions](/developers/monetization/implementing-app-subscriptions) and [Implementing One-Time Purchases](/developers/monetization/implementing-one-time-purchases)
### Subscription Entitlement Migration Guide
Starting on **October 1, 2024**, we will be migrating our existing entitlement system to a new behavior where **entitlements do not expire until explicitly canceled**. This migration guide outlines the changes and impacts of this migration on developers and guides how to manage these changes effectively.
With this update, entitlements for subscription SKUs will no longer emit events when a new subscription billing period begins. If you need to know when a subscription has been renewed, use the new [Subscription API](/developers/resources/subscription) and related [Subscription Gateway Events](/developers/events/gateway-events#subscriptions).
### Current System
Currently, entitlements for Subscription SKUs purchased through Discord have:
* An `ends_at` date that corresponds to the subscription interval. This date is updated at each billing cycle.
* A entitlement `type` value of `APPLICATION_SUBSCRIPTION` (type `8`).
* An `ENTITLEMENT_UPDATE` event is triggered at the start of each new subscription period.
### New System
Post-migration, entitlements for Subscription SKUs purchased through Discord will:
* No longer have an end date (`ends_at` will be `null`) until the user decides to cancel the subscription.
* Now have an entitlement `type` value of `PURCHASE` (type `1`).
* No `ENTITLEMENT_UPDATE` events will be triggered until the subscription is canceled.
### Migration Timeline
* **Migration Start Date:** October 1, 2024
* **Migration End Date:** November 1, 2024
### Migration Impacts
### 1) Existing Entitlements Scheduled to Renew
* **During Migration Window:**
* These will automatically transfer to the new system.
* A new `ENTITLEMENT_CREATE` event will be triggered to indicate the migration. This does not indicate a net new entitlement.
* No further events will be generated until the entitlement ends, which will then trigger an `ENTITLEMENT_UPDATE` event.
* The `ends_at` value in the `ENTITLEMENT_UPDATE` event and in the Entitlement API will indicate the timestamp when the entitlement ends.
### 2) Existing Entitlements Set to End
* **During Migration Window:**
* These entitlements will naturally expire and not renew under the new system.
* No new entitlement events will be generated for these cases.
### Developer Actions
* **Pre-Migration:**
* Review and understand the new entitlement event structure.
* Adjust your system to handle `ends_at` being null, which now indicates an indefinite entitlement.
* Adjust your system not to expect type `APPLICATION_SUBSCRIPTION` (type `8`) for Discord-managed subscription entitlements.
* **Post-Migration:**
* Monitor for `ENTITLEMENT_CREATE`, `ENTITLEMENT_UPDATE`, `SUBSCRIPTION_CREATE`, and `SUBSCRIPTION_UPDATE` events.
* Update any references to an entitlement `ends_at` timestamps, which now indicate the ending of an entitlement. If you need to know when a subscription's period ends, use the [Subscription API](/developers/resources/subscription) and related [Subscription Gateway Events](/developers/events/gateway-events#subscriptions).
## Launching Activities in Response to Interactions
[Activities](/developers/activities/overview) can now be launched as a response to interactions using the `LAUNCH_ACTIVITY` (type `12`) [interaction callback type](/developers/interactions/receiving-and-responding#interaction-response-object-interaction-callback-type). `LAUNCH_ACTIVITY` can be used in response to `APPLICATION_COMMAND`, `MESSAGE_COMPONENT`, and `MODAL_SUBMIT` [interaction types](/developers/interactions/receiving-and-responding#interaction-object-interaction-type).
## Entry Point Commands
Apps with [Activities](/developers/activities/overview) enabled can now create [Entry Point commands](/developers/interactions/application-commands#entry-point-commands) using the `PRIMARY_ENTRY_POINT` (type `4`) [command type](/developers/interactions/application-commands#application-command-object-application-command-types). Apps are limited to one globally-scoped Entry Point command, which appears in the App Launcher.
When creating or updating an Entry Point command, an [Entry Point handler](/developers/interactions/application-commands#application-command-object-entry-point-command-handler-types) can be defined using the [`handler` field](/developers/interactions/application-commands#application-command-object-application-command-structure). The `handler` field determines whether your app wants to manually handle responding to the interaction:
* If the value is `DISCORD_LAUNCH_ACTIVITY` (`2`), Discord will automatically handle the interaction and send a follow-up message to the channel where the Entry Point command was invoked from.
* If the value is `APP_HANDLER` (`1`), your app will receive an interaction token and will be responsible for responding to the interaction. In this case, you can launch your Activity using the `LAUNCH_ACTIVITY` (type `12`) [interaction callback](/developers/interactions/receiving-and-responding#interaction-response-object-interaction-callback-type).
More details about Entry Point commands can be found in the [Application Commands documentation](/developers/interactions/application-commands#entry-point-commands) and in the [Activity development guide](/developers/activities/development-guides/user-actions#setting-up-an-entry-point-command).
### Default Entry Point Commands
Starting today, when you enable Activities in your [app's settings](http://discord.com/developers/applications), a [default Entry Point command](/developers/interactions/application-commands#default-entry-point-command) called "Launch" will automatically be created for your app. This can be customized or deleted like other commands if you want to update the name or handler type.
## Voice Encryption Modes
Added documentation for voice [encryption modes](/developers/topics/voice-connections#transport-encryption-modes) `aead_aes256_gcm_rtpsize` and `aead_xchacha20_poly1305_rtpsize` while announcing the deprecation of all `xsalsa20_poly1305*` variants and `aead_aes256_gcm`. Deprecated encryption modes will be discontinued as of November 18th, 2024.
Deprecated encryption modes will be discontinued as of November 18th, 2024.
## Voice Gateway Version 8 and Deprecation of Versions \< 4
We are officially deprecating some very old voice gateway versions (> 7 years ago).
* The voice gateway now supports a resume which re-sends lost messages. Use voice gateway version 8 and refer to [Buffered Resume](/developers/topics/voice-connections#buffered-resume).
* We have removed the default option for voice gateway version. Once this is deprecated, you must pass a voice gateway version.
You will be required to pass a voice gateway version and deprecated voice gateway versions will be discontinued as of November 18th, 2024. See [Voice Gateway Versioning](/developers/topics/voice-connections#voice-gateway-versioning) for further details.
## Get Guild Role Endpoint
Need to get just one role, not the whole role list? Use the new [Get Guild Role](/developers/resources/guild#get-guild-role) endpoint to fetch a single role by ID.
## User App Install Count
We've added an approximate user install count to the [Application object](/developers/resources/application#application-object) for user-installable apps. You can also view an app's install counts in the developer portal.
## Voice State Endpoints
Voice states can now be accessed over the HTTP API! Apps can use the new [Get Current User Voice State](/developers/resources/voice#get-current-user-voice-state) and [Get User Voice State](/developers/resources/voice#get-user-voice-state) endpoints to fetch a user's voice state without a Gateway connection.
## Supported Activity Types for SET\_ACTIVITY
The [`SET_ACTIVITY` RPC command](/developers/topics/rpc#setactivity) has been updated to support 3 additional [activity types](/developers/events/gateway-events#activity-object-activity-types): Listening (`2`), Watching (`3`), and Competing (`5`). Previously, it only accepted Playing (`0`).
The [Game SDK](/developers/developer-tools/game-sdk#activities) has not been updated to support setting [`ActivityType`](/developers/developer-tools/game-sdk#activitytype-enum), and is still limited to read-only (to handle events that you receive from Discord).
## Application Emoji
You can now upload emojis for your application in your [app's settings](https://discord.com/developers/applications) and use them as custom emojis anywhere on Discord.
* Up to 2000 emojis per app
* Support for user-installable apps
* Can be [managed via the API](/developers/resources/emoji#create-application-emoji) with a bot token
## Activities Proxy CSP Update
This change is outdated. We have since updated the Activities Proxy CSP and the use of `/.proxy/` is no longer required. For the latest information, please refer to [this changelog](/developers/change-log#remove-proxy-from-discord-activity-proxy-path).
This change will be rolled out to all existing applications on **August 28, 2024**.
We will be updating our Content Security Policy (CSP) for the Activities Domain (`https://.discordsays.com`). This represents a **breaking change** for **all Activities**, and as such we have a migration plan in order.
our CSP will be updated as follows:
* all requests must be made through `https://.discordsays.com/.proxy/`, and requests to other paths on the `discordsays.com` domain will be blocked.
* requests to `https://discord.com/api/` will be permitted, but other paths on the `discord.com` domain will be blocked.
* Only allowed paths on `cdn.discordapp.com` and `media.discordapp.net` will be permitted such as `/attachments/`, `/icons/`, and `/avatars/`.
* nested child iframes must also mount paths prepended by `/.proxy/`
As of [embedded-app-sdk v1.4.0](https://github.com/discord/embedded-app-sdk/releases/tag/v1.4.0) we have updated `patchUrlMappings` to automatically route requests through `/.proxy/`, so updating your SDK version calling `patchUrlMappings` is a good first step. If you are unfamiliar with `patchUrlMappings`, please consult the [documentation](/developers/activities/development-guides/networking#using-external-resources).
All Application IDs created after `07/17/2024 12:00:00` UTC (applicationID greater than `1263102905548800000`) will also automatically have the new CSP applied. Testing your production code on a new application created after this date is a suggested way for developers to test compliance with this new CSP.
## Guild Member Banners
Apps can now access guild member profile banners via the API and Gateway! The [guild member object](/developers/resources/guild#guild-member-object) now includes a `banner` field which can be used to create the [guild member banner URL](/developers/reference#image-formatting).
## Message Forwarding rollout
We are slowly rolling out the message forwarding feature to users. This feature allows callers to create a message using `message_reference.type = FORWARD` and have the API generate a `message_snapshot` for the sent message. The feature has [some limitations](/developers/resources/message#message-reference-types) and the snapshot is a minimal version of a standard `MessageObject`, but does capture the core parts of a message.
The resulting message will look something like:
```json theme={"system"}
{
"id": "1255957733279273083",
"message_reference": {
"type": 1, // Forward
...
}
"message_snapshots": [
{
"message": {
"content": "original message",
"embeds": [...],
"attachments": [...],
...
}
}
],
...
}
```
We have applied stricter rate limits for this feature based on the following:
* number of forwards sent by the user
* total attachment size
###### API Updates since preview
This was [previously announced](https://discord.com/channels/613425648685547541/697138785317814292/1233463756160503859) but note that the final API has a few changes since the API was first previewed:
* [`message snapshot`](/developers/resources/message#message-snapshot-object) objects don't include a `guild` field anymore since the `message_reference` already provides that information
* forwarded messages have a distinctive `message_reference` type of `FORWARD` now
## Banners in Get Current User Guilds
[`GET /users/@me/guilds`](/developers/resources/user#get-current-user-guilds) now includes each guild's `banner` field! This enables apps using OAuth2 with the `guilds` scope to display guild banners.
## User-Installed Apps General Availability
Back in March, we announced [the beta for user-installed apps](/developers/change-log#userinstallable-apps-preview). After listening and making updates based on feedback from developers and modmins, we're excited to announce that user-installed apps are now considered generally available and can be used in all servers (regardless of size).
With this update, there are a few API and behavioral updates for user-installed apps.
###### API Updates
* `user_id` has been removed from the `interaction_metadata` field on messages. Instead, you can use the `id` field in the nested `user` object. See the [Message Interaction Metadata Object](/developers/resources/message#message-interaction-metadata-object) for details.
* User-installed apps are now limited to creating a maximum of 5 [follow-ups](/developers/interactions/receiving-and-responding#followup-messages) when responding to interactions. This only affects the [Create Followup Message endpoint](/developers/interactions/receiving-and-responding#create-followup-message), and apps installed to the server are unaffected.
* On [Interactions](/developers/interactions/receiving-and-responding#interaction-object-interaction-structure), the value of `authorizing_integration_owners` is now correctly serialized as a string. Previously, the `"0"` value was incorrectly serialized as a number.
* `app_permissions` on [Interactions](/developers/interactions/receiving-and-responding#interaction-object-interaction-structure) now correctly represents the permissions for user-installed apps. Previously, the value was incorrect for user-installed apps.
* Updating a message can result in a `400` response if the content of the message was blocked by AutoMod, which may be particularly important for [deferred messages](/developers/interactions/receiving-and-responding#responding-to-an-interaction).
* Interaction responses are no longer forced to be ephemeral for servers with over 25 members.
###### New `Use External Apps` Permission
A new [`USE_EXTERNAL_APPS` (`1 << 50`) permission](/developers/topics/permissions#permissions-bitwise-permission-flags) was added, and is enabled for servers by default. The new permission lets modmins control whether user-installed apps can post public replies in a server. If `Use External Apps` is disabled and your app is *not* installed to the server, your app’s responses will be ephemeral for the end user.
Read more in the [Moderating Apps on Discord Help Center article](https://support.discord.com/hc/en-us/articles/23957313048343-Moderating-Apps-on-Discord#h_01HZQQQEADYVN2CM4AX4EZGKHM).
###### Updated Defaults for New Apps
* Newly-created apps now default to having both "User Install" *and* "Guild Install" [installation contexts](/developers/resources/application#installation-context) enabled. This can be updated in the **Installation** tab in an [app's settings](https://discord.com/developers/applications).
* Newly-created apps now default to using the "Discord Provided Link" [install link](/developers/resources/application#install-links). This can be updated in the **Installation** tab in an [app's settings](https://discord.com/developers/applications).
* If Discord Provided Link is selected as the install link type, `application.commands` scope is added to both installation contexts.
## Premium Apps: New Premium Button Style & Deep Linking URL Schemes
**New Premium Button Style**
Introduces a new `premium` [button style](/developers/components/reference#button-button-styles) to be used with a `sku_id` which points to an active [SKU](/developers/resources/sku#sku-object). This allows developers to customize their premium experience by returning specific subscription or one-time purchase products.
Learn more about using [button components with interactions](/developers/components/reference#button).
This change deprecates Interaction Response Type 10
The `PREMIUM_REQUIRED (10)` interaction response type is now deprecated in favor of using custom premium buttons. This will continue to function but may be eventually unsupported. It is recommended to migrate your bots to use the more flexible [premium button component](/developers/components/reference#button-button-styles).
Learn more about [gating features with premium interactions](/developers/monetization/implementing-app-subscriptions#prompting-users-to-subscribe).
**Deep Linking URL Schemes for SKUs and Store**
Introduces two new url schemes for linking directly to the Application Directory. When these links are used in chat, they are rendered as rich embeds that users can interact with to launch an app's store or open a SKU detail modal.
* New [Store URL Scheme](/developers/monetization/managing-skus#linking-to-your-store): `https://discord.com/application-directory/:appID/store`
* New [SKU URL Scheme](/developers/monetization/managing-skus#linking-to-a-specific-sku): `https://discord.com/application-directory/:appID/store/:skuID`
## Auto Moderation Member Profile Rule
* Add Auto Moderation `MEMBER_PROFILE` rule [trigger\_type](/developers/resources/auto-moderation#auto-moderation-rule-object-trigger-types). This rule type will check if a member's profile contains disallowed keywords.
* Add Auto Moderation `BLOCK_MEMBER_INTERACTION` [action type](/developers/resources/auto-moderation#auto-moderation-action-object-action-types) currently available for the `MEMBER_PROFILE` rule [trigger\_type](/developers/resources/auto-moderation#auto-moderation-rule-object-trigger-types). This action will "quarantine" the member to some extent and prevent them from performing most interactions within a specific server.
## Premium Apps: One-Time Purchases and Store
Two new features are now available for Premium Apps: One-Time Purchases and Stores.
**One-Time Purchases**
* **Durable Items**: A one-time purchase that is permanent and is not subject to either renewal or consumption, such as lifetime access to an app's premium features.
* **Consumable Items**: A one-time, non-renewable purchase that provides access, such as a temporary power-up or boost in a game.
Learn more about [Implementing One-Time Purchases](/developers/monetization/implementing-one-time-purchases).
**A Store for Your Premium App**
We have also introduced a Store for your Premium App to showcase your app subscriptions and one-time purchase items. You can now create a unique Store page within the developer portal and add your published subscription SKUs or one-time purchase SKUs to your store view, allowing your users to buy these items from your App Directory or Bot User Profile.
To explore these features, eligibility details, and how to enable monetization for your app, check out the [Monetization Overview](/developers/monetization/overview).
**API Documentation Updates**
The following were added to our public Monetization documentation with this update:
* New [SKU Object Types](/developers/resources/sku#sku-object-sku-types)
* New [Entitlement Object Types](/developers/resources/entitlement#entitlement-object-entitlement-types)
* [Consume an Entitlement](/developers/resources/entitlement#consume-an-entitlement) API endpoint
* `consumed` field on the [Entitlement](/developers/resources/entitlement) resource
## Modify Guild Member flags field permissions
Update permissions necessary to modify the `flags` field when calling the [Modify Guild Member](/developers/resources/guild#modify-guild-member) endpoint.
## CSV Export for Premium App Analytics
For apps with [Monetization](/developers/monetization/overview) enabled, we have released the ability to export your SKU analytics to CSV. These exports allow you to use your preferred data tools to report on your premium offerings.
You can find the export at the bottom of the `Monetization → Analytics` tab of your app to export data points such as `sales_count`, `sales_amount`, `sales_currencies`, `cancellation_count`, `refund_amount`, and `refund_count`, aggregated by each of your offerings for the selected month.
## Discord Activities: Developer Preview of the Embedded App SDK
Discord Developers can now build Activities!
Activities are interactive, multiplayer experiences that run in an iframe in Discord. In order to make the communication between your experience and Discord, we've introduced the Embedded App SDK to assist in communicating between your app and the Discord client.
* New [Discord Activities](/developers/activities/overview) developer docs with a tutorial, code samples, development guides, and design principles.
* The Embedded App SDK is now available via [npm](https://npmjs.com/package/@discord/embedded-app-sdk) and [GitHub](http://github.com/discord/embedded-app-sdk).
* The [Embedded App SDK Reference](/developers/developer-tools/embedded-app-sdk) is now available.
To learn more about how to get started building your own Activity, check out the [Activities Overview](/developers/activities/overview).
## User-Installable Apps Preview
Apps can now be installed to users—making them easier to install, discover, and access across Discord. User-installed apps can be used across all of a user's servers, within their (G)DMs, and in DMs with the app's bot user.
When creating or updating your app, you can choose which installation types your app supports on the **Installation** page in your [app's settings](https://discord.com/developers/applications). To quickly get started, you can follow the new [Developing a User-Installable App tutorial](/developers/tutorials/developing-a-user-installable-app) or read details about the new changes below.
This change introduces new concepts and fields across the API that apps will now encounter.
###### API Changes
**Concepts:**
* [Installation context](/developers/resources/application#installation-context) defines how an app was installed: to a user, a guild (server), or both. Currently, apps will default to only support the guild installation context, but the default may change in the future.
* Commands can also support one or both installation contexts, with the default being the same as the app's supported installation context(s) at the time of command creation.
* [Interaction context](/developers/interactions/application-commands#interaction-contexts) defines where a command can be used in Discord—within guilds, DM with your app's bot user, and/or within group DMs and DMs other than with your app's bot user.
* The installation flow for apps have been updated so users can select whether they want to install an app to their account or to a server.
**API Fields:**
* New `integration_types_config` field for [Applications](/developers/resources/application#application-object) include the default scopes and permissions for app's supported installation contexts
* New `integration_types` and `contexts` fields for [Commands](/developers/interactions/application-commands#application-command-object-application-command-structure) are the supported [installation](/developers/interactions/application-commands#installation-context) and [interaction](/developers/interactions/application-commands#interaction-contexts) contexts (respectively) for the command. Read [command contexts](/developers/interactions/application-commands#contexts) documentation for details.
* New `context` field for [Interactions](/developers/interactions/receiving-and-responding#interaction-object-interaction-structure) indicates the [interaction context](/developers/interactions/application-commands#interaction-contexts) where an interaction was triggered from.
* New `authorizing_integration_owners` field for [Interactions](/developers/interactions/receiving-and-responding#interaction-object-interaction-structure) includes a mapping of installation contexts that the interaction was authorized for, to related snowflakes for that context. Read [Authorizing Integration Owners Object](/developers/interactions/receiving-and-responding#interaction-object-authorizing-integration-owners-object) for details.
* `app_permissions` is now always serialized for interactions to indicate what [permissions](/developers/topics/permissions#permissions-bitwise-permission-flags) your app has access to in the context its' responding. For (G)DMs with other users, it will include the `ATTACH_FILES | EMBED_LINKS | MENTION_EVERYONE`, and for DMs with the app's bot user it will also contain `USE_EXTERNAL_EMOJIS` for the bot’s DM
* New `interaction_metadata` on [Messages](/developers/resources/message#message-object) that are created as part of an interaction response (either a response or follow-up). See [Message Interaction Metadata Object](/developers/resources/message#message-interaction-metadata-object) for details.
* `dm_permission` field for [Commands](/developers/interactions/application-commands#application-command-object-application-command-structure) is deprecated. Apps should use `contexts` instead.
* `interaction` field for [Messages](/developers/resources/message#message-object) is deprecated. Apps should use `interaction_metadata` instead.
###### Limitations and Known Issues
* During the preview, interaction responses for the user installation context will be forced to be ephemeral in servers with over 25 members. Forced ephemerality is enforced at the client-level, so your app does not need to manually pay attention to server size, and will not receive errors via the API.
* All [follow-up messages](/developers/interactions/receiving-and-responding#followup-messages) are currently forced to be ephemeral in DMs
* Follow-up messages have a bug where they will not correctly respect user permissions
## Guild Prune Requiring
The [Get Guild Prune Count](/developers/resources/guild#get-guild-prune-count) and [Begin Guild Prune](/developers/resources/guild#begin-guild-prune)
endpoints now require the `MANAGE_GUILD` permission alongside the existing `KICK_MEMBERS` requirement.
## Enforced Nonces on Create Message Endpoint
The [Create message](/developers/resources/message#create-message) endpoint now supports an `enforce_nonce` parameter. When set to true, the message will be deduped for the same sender within a few minutes. If a message was created with the same nonce, no new message will be created and the previous message will be returned instead. This behavior will become the default for this endpoint in a future API version.
## Limit Number of Fields in Embeds
[Embed objects](/developers/resources/message#embed-object) are now limited more explicitly to 25 [embed fields](/developers/resources/message#embed-object-embed-field-structure). If you pass more than 25 fields within the an embed's `fields` property, an error will be returned.
Previously, only the first 25 embed fields would be displayed within the embed but no error was returned.
## Clarification on Permission Splits for Expressions and Events
The existing behavior for `MANAGE_GUILD_EXPRESSIONS` and `MANAGE_EVENTS` will **not be changing**. These permissions will continue to allow your bot users to create, update and delete expressions/events. No action will be needed if you plan to continue using these permissions.
To support added controls for expressions and events, new [permissions](/developers/topics/permissions) were added for users and roles in July 2023:
* `CREATE_GUILD_EXPRESSIONS`: `1 << 43`
* `CREATE_EVENTS`: `1 << 44`
These allow for creating new expressions and events, as well as editing and deleting those created by the current user.
These were rolled out in July 2023 to users and roles and have been added to our developer documentation but **are not yet available to app developers**. We will share an update here when these new permissions are available in your apps.
## Experimenting with End-to-End Encryption for Voice & Video
#### What’s Happening?
As outlined in [a blog post earlier this year](https://discord.com/blog/encryption-for-voice-and-video-on-discord), we are experimenting with end-to-end encryption (e2ee) for voice and video channels.
End-to-end encryption is designed to only allow the participants in a call to decipher its contents. One of the protocols we’re experimenting with is called Messaging Layer Security, which we believe would allow us to deliver end-to-end encryption at scale. Intermediaries, including platforms like Discord, are unable to access the content of communications encrypted with end-to-end encryption.
#### How do I prepare for the changes?
During this testing phase, there is nothing developers need to do to support end-to-end encryption. Voice channels will automatically downgrade to documented, non-e2ee protocols when a bot user joins the channel. This is transparent to the connecting client but may result in a slight delay between establishing a connection and receiving audio.
#### What is planned for the future?
We will be continuing our testing and will share updates along with developer documentation and sample code once it is available.
Once this information is published, we will provide developers with a substantial timeframe to implement end-to-end encryption when interacting with voice and video.
## Premium App Subscriptions: New Ways for Testing App Subscriptions
Following feedback on Premium App Subscriptions, we've made it easier for developers to test their app subscriptions. The goal is to provide you with flexibility during testing and prevent you from having to use live payment methods.
* Team members will automatically receive a 100% discount on a subscription for your app, allowing you to test the end-to-end payment flow
* Developers can create and delete [test entitlements](/developers/resources/entitlement#create-test-entitlement) to toggle access to an application's premium features
Read more about [Testing your App Subscriptions Implementation](/developers/monetization/implementing-app-subscriptions#testing-your-app-subscription-implementation) for details.
## Fix Message Edit Interaction Response Permissions
Behavior for message edit interaction response actions like [updating interaction responses](/developers/interactions/receiving-and-responding#edit-original-interaction-response) and [sending follow-up messages](/developers/interactions/receiving-and-responding#followup-messages) have been updated to follow a bot user's permissions.
Previously, some message edit interaction response actions would use the default permissions rather than a bot user's permissions.
## Premium App Subscriptions Now Available in the EU and UK
Starting today, eligible developers based in EU and UK can now monetize their verified apps with App Subscriptions. [App Subscriptions](/developers/monetization/overview) let you to charge your users for premium functionality with a recurring, monthly subscription.
New features for Premium App Subscriptions are documented in the [App Subscriptions overview](/developers/monetization/overview) and in [the changelog for the previous App Subscriptions release](/developers/change-log#premium-app-subscriptions-available-in-the-us).
To learn more about eligibility details and how to enable monetization for your app, check out the [Monetization Overview](/developers/monetization/overview).
## Global Rate Limit added to discordapp.com/\*
We have added a global rate limit for API requests made to `discordapp.com/*` and may further restrict requests in the future.
To limit impact on your app, please make sure you are making calls to `discord.com/*`.
This does **not** apply for `cdn.discordapp.com`.
Refer to the [API Reference](/developers/reference) for more info on which url(s) to use when building on the REST API
* [February 14, 2022 Change Log](/developers/change-log#api-v10): Requests to v10 and higher will no longer be supported on `discordapp.com` (this does not affect `cdn.discordapp.com`)
* [May 4, 2020 #api-announcements](https://discord.com/channels/613425648685547541/697138785317814292/706944540971630662)
## Premium App Subscriptions Available in the US
Starting today, eligible US-based developers can monetize their verified apps with App Subscriptions. [App Subscriptions](/developers/monetization/overview) let you to charge your users for premium functionality with a recurring, monthly subscription.
* Manage subscription SKUs in the Developer Portal
* View monetization analytics in the Developer Portal
* Team owners can setup and manage payouts in Developer Portal
* New endpoints for working with [SKUs](/developers/resources/sku) and [Entitlements](/developers/resources/entitlement):
* [List SKUs](/developers/resources/sku#list-skus) `GET /applications//skus`
* [List Entitlements](/developers/resources/entitlement#list-entitlements) `GET /applications//entitlements`
* [Create Test Entitlement](/developers/resources/entitlement#create-test-entitlement) `POST /applications//entitlements`
* [Delete Test Entitlement](/developers/resources/entitlement#delete-test-entitlement) `DELETE /applications//entitlements/`
* [Gateway Events](/developers/events/gateway-events#entitlements) for working with entitlements: `ENTITLEMENT_CREATE`, `ENTITLEMENT_UPDATE`, `ENTITLEMENT_DELETE`
* New [`PREMIUM_REQUIRED (10)` interaction response type](/developers/interactions/receiving-and-responding#interaction-response-object-interaction-callback-type) is available to prompt users to upgrade
* New `entitlements` field, which is an array of [entitlement](/developers/resources/entitlement) objects, available in interaction data payloads when [receiving and responding to interactions](/developers/interactions/receiving-and-responding#interaction-object-interaction-structure)
To learn more about eligibility details and how to enable monetization for your app, check out the [Monetization Overview](/developers/monetization/overview).
## Default Value in Auto-populated Select Menus
A new `default_values` field was added for user (`5`), role (`6`), mentionable (`7`), and channel (`8`) [select menu components](/developers/components/reference). `default_values` is a list of [default value objects](/developers/components/reference#user-select-select-default-value-structure), which each include an `id` (the snowflake value for the resource), as well as a corresponding `type` (either `"user"`, `"role"`, or `"channel"`).
## Team Member Roles
You can now select roles other than admin when inviting users or configuring members of a team. There are four [role types](/developers/topics/teams#team-member-roles-team-member-role-types) that a team member can be assigned: owner, admin, developer, or read-only. The team member object now has an additional [`role` field](/developers/topics/teams#data-models-team-member-object), which is a string representing the member's current role.
Details about team member roles are in the updated [Teams documentation](/developers/topics/teams#team-member-roles).
## Embed Debugger
We've released a new [Embed Debugger tool](https://discord.com/developers/embeds) that shows you how a URL's metadata will be parsed and rendered as a link embed within the Discord client. Use it to preview your site's embed, or debug why your site's link embed isn't working as expected.
## Activity State for Bot Users
The `state` field in [activity objects](/developers/events/gateway-events#activity-object) can now be set when [updating presence](/developers/events/gateway-events#update-presence) for a bot user. The value of `state` will appear as a custom status for the bot user when an [activity's `type`](/developers/events/gateway-events#activity-object-activity-types) is set to `4`, or as additional data under an activity's name for other activity types.
## Public Preview of OpenAPI 3.1 Specification
We're introducing an [OpenAPI 3.1 spec](https://github.com/discord/discord-api-spec) in public preview to make it easier and more reliable to develop with the HTTP API. While our current developer documentation requires manual reviews and updates, the OpenAPI spec is generated from the source code which means it better reflects the nooks, crannies, and nuances of the Discord API.
The public preview of the OpenAPI spec is subject to breaking changes without advance notice, and should not be used within production environments. If you see something that looks incorrect or can be improved, you can [open an issue](https://github.com/discord/discord-api-spec/issues).
The public spec can be found in the new [`discord-api-spec` repository on GitHub](https://github.com/discord/discord-api-spec).
## New GUILD\_MEDIA channel type
* Add the [`GUILD_MEDIA` (16) channel type](/developers/resources/channel#channel-object-channel-types). `GUILD_MEDIA` channels only support threads, similar to `GUILD_FORUM` channels.
Read the [media channel topic](/developers/topics/threads#media-channels) for more information on the relevant APIs and technical details, or the [media channel Help Center Article](https://creator-support.discord.com/hc/en-us/articles/14346342766743) for more about the feature.
## Add Join Raid and Mention Raid fields
* Add Auto Moderation `mention_raid_protection_enabled` [trigger\_metadata](/developers/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata) field for the `MENTION_SPAM` [trigger\_type](/developers/resources/auto-moderation#auto-moderation-rule-object-trigger-types). If this field and its parent `MENTION_SPAM` rule are enabled, Auto Moderation provides baseline detection against sudden spikes in mention activity that are normally indicative of mention raids.
* Add `safety_alerts_channel_id` [guild](/developers/resources/guild#guild-object) field and [`RAID_ALERTS_DISABLED` guild feature flag](/developers/resources/guild#guild-object-guild-features) which are associated with join raid protection
## Unique usernames on Discord
Bot users will stay on the legacy username system for now. More details can be found on the [Developer Help Center article](https://dis.gd/app-usernames).
Discord’s username system is changing. Discriminators are being removed and new, unique usernames and display names are being introduced. You can read more details about how changes to the username system affects non-bot users in the [general Help Center article](https://dis.gd/usernames). To learn how it impacts bot users specifically, you can read the [Developer Help Center article](https://dis.gd/app-usernames).
This changelog focuses only on the technical changes to be aware of to update your app's code.
### Identifying migrated users
The new username system will rollout to users over time rather than all at once. The value of a single zero (`"0"`) in the [`discriminator` field](/developers/resources/user#user-object-user-structure) on a user will indicate that the user has been migrated to the new username system. Note that the discriminator for migrated users will *not* be 4-digits like a standard discriminator (it is `"0"`, not `"0000"`). The value of the `username` field will become the migrated user's unique username.
After migration of all users is complete, the `discriminator` field may be removed.
#### Example migrated user
```json theme={"system"}
{
"id": "80351110224678912",
"username": "nelly",
"discriminator": "0",
"global_name": "Nelly",
"avatar": "8342729096ea3675442027381ff50dfe",
"verified": true,
"email": "nelly@discord.com",
"flags": 64,
"banner": "06c16474723fe537c283b8efa61a30c8",
"accent_color": 16711680,
"premium_type": 1,
"public_flags": 64
}
```
### Display names
As part of the new username system, standard Discord users can define a non-unique display name. This value will be a new `global_name` field with a max length of 32 characters. If the user has not set a display name, `global_name` will be null.
### Default avatars
For users with migrated accounts, default avatar URLs will be based on the user ID instead of the discriminator. The URL can now be calculated using `(user_id >> 22) % 6`. Users on the legacy username system will continue using `discriminator % 5`.
## Bot users added to all new apps
Starting today, [bot users](/developers/topics/oauth2#bot-vs-user-accounts) will be added to all newly-created apps. Settings and configuration options for bot users remain the same, and can still be accessed on the **Bot** page within your [app's settings](https://discord.com/developers/applications).
If your app doesn't need or want a bot user associated with it, you can refrain from adding the [`bot` scope](/developers/topics/oauth2#shared-resources-oauth2-scopes) when installing your app.
## Interaction Channel Data
Interactions now contain a `channel` field which is a partial channel object and guaranteed to contain `id` and `type`. We recommend that you begin using this channel field to identify the source channel of the interaction, and may deprecate the existing `channel_id` field in the future. See the [interaction documentation](/developers/interactions/receiving-and-responding#interaction-object) for more details.
## Add Auto Moderation custom\_message Action Metadata Field
Add new `custom_message` [action metadata](/developers/resources/auto-moderation#auto-moderation-action-object-action-metadata) for the `BLOCK_MESSAGE` [action type](/developers/resources/auto-moderation#auto-moderation-action-object-action-types)). You can now specify a custom string for every Auto Moderation rule that will be shown to members whenever the rule blocks their message. This can be used as an additional explanation for why a message was blocked and as a chance to help members understand your server's rules and guidelines.
## Update to Locked Threads
### Upcoming Changes
Currently, threads in Discord (including forum posts) can either be archived or both locked and archived. Starting on **March 6, 2023**, threads will be able to be locked *without* being archived, which will slightly change the meaning of the [`locked` field](/developers/resources/channel#thread-metadata-object-thread-metadata-structure).
`locked` currently indicates that a thread cannot be reopened by a user without the [`MANAGE_THREADS` (`1 << 34`) permission](/developers/topics/permissions#permissions-bitwise-permission-flags), but it doesn't restrict user activity within active (meaning non-archived) threads. After this change, users (including bot users) without the `MANAGE_THREADS` permission will be more restricted in locked threads. Users won't be able to create or update messages in locked threads, or update properties like its title or tags. Additionally, some user activity like deleting messages and adding or removing reactions will *only* be allowed in locked threads if that thread is also active (or un-archived).
If a user or bot user has the `MANAGE_THREADS` permission, they will still be able to make changes to the thread and messages. The upcoming change does not affect the meaning of the [`archived` field](/developers/resources/channel#thread-metadata-object-thread-metadata-structure) or the behavior of a thread that is both locked and archived.
### How do I prepare for this change?
If your app is interacting with threads (including forum posts), it should check the state of the `locked` and/or `archived` field for the thread to understand which actions it can or cannot perform. It should also be prepared to handle any errors that it may receive when a thread is locked.
## Increase Auto Moderation Keyword Limits
* Increase maximum number of rules with `KEYWORD` [trigger\_type](/developers/resources/auto-moderation#auto-moderation-rule-object-trigger-types) per guild from 5 to 6
* Increase maximum length for each keyword in the `keyword_filter` and `allow_list` [trigger\_metadata](/developers/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata) fields from 30 to 60.
## Guild Audit Log Events
At long last, a new [`GUILD_AUDIT_LOG_ENTRY_CREATE`](/developers/events/gateway-events#guild-audit-log-entry-create) event has been added to the gateway, allowing your application to react to moderation actions in guilds. The `VIEW_AUDIT_LOG` permission is required in order to receive these events, and the [`GUILD_MODERATION` intent](/developers/events/gateway#gateway-intents) needs to be set when connecting to the gateway.
## Thread Member Details and Pagination
A new `member` field was added to the [thread member object](/developers/resources/channel#thread-member-object). `member` is a [guild member object](/developers/resources/guild#guild-member-object) that will be included within returned thread member objects when the new `with_member` field is set to `true` in the [List Thread Members](/developers/resources/channel#list-thread-members) (`GET /channels//thread-members`) and [Get Thread Member](/developers/resources/channel#get-thread-member) (`GET /channels//thread-members/`) endpoints.
Setting `with_member` to `true` will also enable pagination for the [List Thread Members](/developers/resources/channel#list-thread-members) endpoint. When the results are paginated, you can use the new `after` and `limit` fields to fetch additional thread members and limit the number of thread members returned. By default, `limit` is 100.
#### Upcoming Changes
Starting in API v11, [List Thread Members](/developers/resources/channel#list-thread-members) (`GET /channels//thread-members`) will *always* return paginated results, regardless of whether `with_member` is passed or not.
## Add Default Layout setting for Forum channels
`default_forum_layout` is an optional field in the [channel object](/developers/resources/channel) that indicates the default layout for posts in a [forum channel](/developers/topics/threads#forums). A value of 1 (`LIST_VIEW`) indicates that posts will be displayed as a chronological list, and 2 (`GALLERY_VIEW`) indicates they will be displayed as a collection of tiles. If `default_forum_layout` hasn't been set, the value will be `0`.
Setting `default_forum_layout` requires the `MANAGE_CHANNELS` permission.
## Add Application Connections Metadata and Linked Roles
Introducing [linked roles](https://discord.com/blog/connected-accounts-functionality-boost-linked-roles) as well as the ability for all developers to set up their own linked roles with an application. This includes:
* New [`role_connections_verification_url`](/developers/resources/application#application-object) that can be set in the developer portal in order for the application to render as potential verification option for linked roles.
* [Application metadata](/developers/resources/application-role-connection-metadata#application-role-connection-metadata-object) to specify more detailed linked role requirements.
* New endpoints to [retrieve](/developers/resources/application-role-connection-metadata#get-application-role-connection-metadata-records) (`GET /applications//role-connections/metadata`) and [update](/developers/resources/application-role-connection-metadata#update-application-role-connection-metadata-records) (`PUT /applications//role-connections/metadata`) application connection metadata.
* New [`role_connections.write`](/developers/topics/oauth2#shared-resources-oauth2-scopes) OAuth2 scope required to authenticate the below requests.
* Endpoints to [retrieve](/developers/resources/user#get-current-user-application-role-connection) (`GET /users/@me/applications/{application.id}/role-connection`) and [update](/developers/resources/user#update-current-user-application-role-connection) (`PUT /users/@me/applications/{application.id}/role-connection`) a user's role connections, both of which return an [application role connection](/developers/resources/user#application-role-connection-object) object.
For a quick rundown on how to get started using linked roles, refer to the [tutorial](/developers/tutorials/configuring-app-metadata-for-linked-roles).
## Add Auto Moderation Allow List for Keyword Rules and Increase Max Keyword Rules Per Guild Limit
* Auto Moderation rules with [trigger\_type](/developers/resources/auto-moderation#auto-moderation-rule-object-trigger-types) `KEYWORD` now support an `allow_list` field in its [trigger\_metadata](/developers/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata). Any message content that matches an `allow_list` keyword will be ignored by the Auto Moderation `KEYWORD` rule. Each `allow_list` keyword can be a multi-word phrase and can contain [wildcard symbols](/developers/resources/auto-moderation#auto-moderation-rule-object-keyword-matching-strategies).
* Increase maximum number of rules with `KEYWORD` [trigger\_type](/developers/resources/auto-moderation#auto-moderation-rule-object-trigger-types) per guild from 3 to 5
* Increase maximum length for each regex pattern in the `regex_patterns` [trigger\_metadata](/developers/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata) field from 75 to 260.
## Upcoming Application Command Permission Changes
Based on feedback, we’re updating permissions for [application commands](/developers/interactions/application-commands) to simplify permission management and to make command permissions more closely resemble other permissions systems in Discord.
Server admins can begin to opt-in to the command permission changes outlined here on a per-server basis **starting on December 16, 2022**. However, changes will not be applied to all servers **until late January or early February**.
Current permissions behavior is documented in [the application commands documentation](/developers/interactions/application-commands#permissions) and in [the changelog for the previous permissions update](/developers/change-log#updated-command-permissions)
These changes are focused on how configured permissions are used by Discord clients, so most apps will be unaffected. However, if your app uses the [Update Permissions endpoint](/developers/interactions/application-commands#edit-application-command-permissions) (`PUT /applications//guilds//commands//permissions`), you may need to make updates and should read these changes carefully.
#### Types of command permission configurations
The following information isn’t changing, but it’s helpful context to understand the changes.
Discord’s clients determine whether a user can see or invoke a command based on three different permission configurations:
* **Command-level permissions** are set up by an admin for a specific *command* in their server. These permissions affect only a specific command.
* **App-level permissions** are set up by an admin for a specific *app* in their server. These permissions affect all commands for an app.
* **`default_member_permissions`** are set up by an app when creating or updating a command. `default_member_permissions` apply to that command in *all* servers (unless an override exists). More information about `default_member_permissions` is [in the documentation](/developers/interactions/application-commands#application-command-permissions-object-using-default-permissions).
The concepts of these permission configurations are not changing. But then of course, the question becomes…
### What's changing?
There are two changes around command permissions:
1. The logic used to apply permission configurations to a user in a given context within Discord clients
2. New `APPLICATION_COMMAND_PERMISSIONS_V2` guild feature flag to indicate whether that guild is using the old permissions logic or the new (upcoming) logic.
Let's go deeper into both of these.
#### 1. How permission configurations are applied in Discord
##### Current behavior:
Currently, these systems are **mutually-exclusive**, meaning that only one type of permission configuration is used to determine whether a user can invoke a command.
With this current system, there is a clear hierarchy: command-level permission configurations take precedence (if present), then app-level permission configurations (if present), and finally `default_member_permissions` if neither are present.
The implication of the current permissions system means that:
* If any command-level permissions are configured, all app-level permissions and `default_member_permissions` are ignored for that command.
* If any app-level permissions are configured, `default_member_permissions` is ignored for *all* of that app’s commands.
This system leads to unintentional permission escalations, and can force moderators to manually re-define their app-level configurations to make small tweaks on the command-level.
##### Upcoming behavior:
The new system removes the mutual exclusion aspect, meaning that the different types of permission configurations work together rather than independently—specifically, more than one may be used to determine whether a user can invoke a command.
**`default_member_permissions` continues to act as a “default” that a developer can set when creating or updating a command.**
**App-level permission configurations now act as the "base" configuration.**
App-level configurations define who is allowed to use the app and where. These will work *together* with `default_member_permissions`, meaning if a user is granted access via an app-level permission configuration, they will still be restricted to the `default_member_permissions` for each command (by default). No more accidentally granting `/ban` which requires `BAN_MEMBERS` to `@BotMemers` just because you gave them access to the app!
**Command-level permission configurations now act as an “override” of the app-level.**
Command-level configurations override what is present at the app-level *and* any restrictions set by `default_member_permissions`. This means that an admin can explicitly grant a user access to a specific command even if they are denied access on the app-level *or* if they don't have permissions that meet that command's `default_member_permissions`.
If a command-level configuration does not exist for the given context, the system will fall back to looking at the app-level configuration.
##### Flowchart for command permissions logic
Below is a simplified flowchart that illustrates how permissions will be applied by the Discord client after the new changes take effect.
#### 2. `APPLICATION_COMMAND_PERMISSIONS_V2` Guild Feature
We added a new [`APPLICATION_COMMAND_PERMISSIONS_V2` feature flag](/developers/resources/guild#guild-object-guild-features) which indicates whether that server is using **the current permissions logic**.
* If the flag *is* present, that server is using the old command permissions behavior.
* If the flag *is not* present, that server has migrated from the old command permissions behavior to the new behavior.
### Am I affected?
Your app will only be affected if it uses the [`PUT /applications//guilds//commands//permissions`](/developers/interactions/application-commands#edit-application-command-permissions) endpoint. This is a pretty restricted endpoint used to manage and update application command permissions on behalf of admins, meaning that it requires the `applications.commands.permissions.update` scope.
**If your app doesn’t use this endpoint, there’s nothing you need to prepare for these changes.**
If your app does use this endpoint, you should read the section on preparing for changes below.
### How do I prepare for the changes?
To prepare for these changes, you should take two steps:
**1. Use the `APPLICATION_COMMAND_PERMISSIONS_V2` flag**
Use this flag to determine which permissions logic that server is using. While the transition from the old behavior to the new behavior is happening, you may need two code paths depending on if the flag is present or not.
```py theme={"system"}
if 'APPLICATION_COMMAND_PERMISSIONS_V2' in guild.features:
# Use current behaviors when interacting with endpoint
else:
# Use new permissions behaviors when interacting with endpoint
```
If you don’t have access to guild features already through Gateway events, you can fetch that information using the [`GET /guilds/` endpoint](/developers/resources/guild#get-guild).
**2. Modify the behavior based on your use case**
After you know what permissions behavior the server is using, you should update how you handle that server specifically.
To understand what changes you need to make, you should look at the assumptions users have when your app updates their server’s commands permissions. Do you have a web dashboard where admins update permissions? If so, analyze the logic of that dashboard and what your permission configurations are trying to do to map them to the new permissions behavior. Do you document what your app is doing in regards to certain command permissions you’re configuring on behalf of the admin? If so, map that documentation to the new behavior.
If you are unsure, you can communicate with your admin users to ask if your new logic meets their expectations.
#### What happens if I don’t update my app?
If your app is affected and you don’t update it, permissions behavior that your app configures may not match what you or the users of your app expect.
#### How long do I have to update my app?
The new `APPLICATION_COMMAND_PERMISSIONS_V2` flag is already live, and you should start seeing it in guilds’ feature flags.
The new permissions behavior will roll out **on December 16, 2022**. On this date, admins will begin to see a banner that allows them to *optionally* move their server to the new behavior.
In **late January or early February**, all servers will be migrated to the new behavior. We'll post another changelog at this point, at which time you can remove any logic around the old permissions behavior.
## GameSDK Feature Deprecation
To help keep us focused on the features, improvements, and gaming-related experiences that Discord users love, we are deprecating the following pieces of the GameSDK **starting today**, and decommissioning them on **Tuesday, May 2, 2023**:
* [Achievements](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/docs/game_sdk/Achievements.md)
* [Applications](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/docs/game_sdk/Applications.md)
* [Voice](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/docs/game_sdk/Discord_Voice.md)
* [Images](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/docs/game_sdk/Images.md)
* [Lobbies](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/docs/game_sdk/Lobbies.md)
* [Networking](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/docs/game_sdk/Networking.md)
* [Relationships](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/docs/game_sdk/Relationships.md)
* [Storage](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/docs/game_sdk/Storage.md)
* [Store](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/docs/game_sdk/Store.mdmd)
This deprecation period will last until **Tuesday May 2, 2023**, after which these pieces will be decommissioned and no longer work. The other pieces of the GameSDK will continue to be supported.
We know that Discord is an important place for people to find belonging, and that using your Discord identity in games is a crucial part of that sense of belonging. You’ll still be able to use the GameSDK to integrate Rich Presence, relationships, entitlements, basic user information, and the overlay.
## Add Auto Moderation Regex Support
Auto Moderation rules with [trigger\_type](/developers/resources/auto-moderation#auto-moderation-rule-object-trigger-types) `KEYWORD` now support
a `regex_patterns` field in its [trigger\_metadata](/developers/resources/auto-moderation#auto-moderation-rule-object-trigger-types).
Regex patterns are a powerful way to describe many keywords all at once using one expression. Only Rust flavored regex is supported, which can be tested in online editors such as [Rustexp](https://rustexp.lpil.uk/).
## Delete Ephemeral Messages
Ephemeral interaction responses and follow-ups can now be deleted with a valid interaction token using the following endpoints:
* [`DELETE /webhooks///messages/@original`](/developers/interactions/receiving-and-responding#delete-original-interaction-response)
* [`DELETE /webhooks///messages/`](/developers/interactions/receiving-and-responding#delete-followup-message)
As a reminder, interaction tokens stay valid for up to 15 minutes after the interaction occurs. Details can be found in the [interaction documentation](/developers/interactions/receiving-and-responding).
## New Select Menu Components
Four new select menu [component types](/developers/components/reference#component-object-component-types) have been added to make it easier to populate selects with common resources in Discord:
* User select (type `5`)
* Role select (type `6`)
* Mentionable (user *and* role) select (type `7`)
* Channel select (type `8`)
The new select menu components are defined similarly to the existing string select menu—with the exception of not including the `options` field and, within channel select menus, having the option to include a `channel_types` field. The [select menu interaction](/developers/components/reference#string-select-string-select-interaction-response-structure) apps receive also contain a [`resolved` field](/developers/interactions/receiving-and-responding#interaction-object-resolved-data-structure) for the new components.
More details can be found in the updated [select menu documentation](/developers/components/reference#component-object-component-types).
## Default Sort Order for Forum Channels
`default_sort_order` is an optional field in the [channel object](/developers/resources/channel) that indicates how the threads in a [forum channel](/developers/topics/threads#forums) will be sorted for users by default. Setting `default_sort_order` requires the `MANAGE_CHANNELS` permission.
If `default_sort_order` hasn't been set, its value will be `null`.
## Auto Moderation Spam and Mention Spam Trigger Types
Two new [trigger types](/developers/resources/auto-moderation#auto-moderation-rule-object-trigger-types) were added to Auto Moderation:
* `MENTION_SPAM` blocks messages that mention more than a set number of unique server members or roles. Apps can define the number (up to 50) using the `mention_total_limit` field in the [trigger metadata object](/developers/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata) when creating or updating an Auto Moderation rule.
* `SPAM` blocks links and messages that are identified as spam.
More information can be found in the [Auto Moderation documentation](/developers/resources/auto-moderation).
## Forum Channels Release
Forum channels ([`GUILD_FORUM` or `15`](/developers/resources/channel#channel-object-channel-types)) have been released to all community servers. `GUILD_FORUM` channels are a new channel type that only supports threads, which display differently than in text (`GUILD_TEXT`) channels.
Check out the [forums topic](/developers/topics/threads#forums) for more information on the relevant APIs and technical details, and the [Forums FAQ](https://support.discord.com/hc/en-us/articles/6208479917079-Forum-Channels-FAQ#h_01G69FJQWTWN88HFEHK7Z6X79N) for more about the feature.
## Message Content is a Privileged Intent
As of today, [message content](/developers/events/gateway#message-content-intent) is a privileged intent for all verified apps *and* apps eligible for verification. More details about why it's becoming a privileged intent and how to apply for it is in the [Help Center FAQ](https://support-dev.discord.com/hc/articles/4404772028055-Message-Content-Privileged-Intent-FAQ).
Any app that does not have the message content intent configured in its app's settings within the Developer Portal will receive empty values in fields that expose message content across Discord's APIs (including the `content`, `embeds`, `attachments`, and `components` fields). These restrictions do not apply for messages that a bot or app sends, in DMs that it receives, or in messages in which it is mentioned.
#### If your app is verified
Verified apps and verification-eligible apps must be approved for the message content intent to receive message content. If your verified app isn’t approved, or doesn’t account for the new message content restrictions, it will break for users.
##### Temporary Message Content Intent
Verified apps or apps that have submitted for verification can temporarily opt-in to a grace period which will allow your app to continue receiving message content until October 1. However, if you opt-in to the grace period, your app will be prevented from joining any additional servers until you opt-out. More details are in the [Help Center article](https://support-dev.discord.com/hc/en-us/articles/8561391080471).
#### If your app is unverified
Unverified apps must still must enable the intent in your app’s settings within the Developer Portal.
Existing unverified apps will automatically have the message content intent toggled on in their settings. New unverified apps will have to manually toggle the intent in the Developer Portal.
## Slash Command Mentions
This week, [Slash Command mentions](/developers/reference#message-formatting) are rolling out across all Discord clients (for Android, mentions are limited to the [React Native client](https://discord.com/blog/android-react-native-framework-update)). Clicking a Slash Command mention will auto-populate the command in the user's message input.
Slash Command mentions use the following format: ``. You can also use `` and `` for subcommands and subcommand groups.
## Session-specific Gateway Resume URLs
Starting on **September 12, 2022**, apps that aren’t using the new `resume_gateway_url` field to resume gateway sessions will be disconnected significantly faster than normal.
A new `resume_gateway_url` field has been added to the [Ready](/developers/events/gateway-events#ready) gateway event to support session-specific gateway connections. The value of `resume_gateway_url` is a session-specific URL that should be used when resuming the gateway session after a disconnect. Previously, `wss://gateway.discord.gg` was used to connect *and* resume sessions, but should now only be used during the connection.
At the moment, the value of `resume_gateway_url` will always be `wss://gateway.discord.gg` to give developers more time to adopt the new field. In the near future, the value will change to the session-specific URLs.
## Upcoming Permissions Change to Webhook Routes
On August 8th, 2022 we will begin requiring the `VIEW_CHANNEL (1 << 10)` permission for webhook routes which require `MANAGE_WEBHOOKS (1 << 29)`, to align with our documented behavior. We don't expect that many applications will be affected by this, but in case you are, please ensure you have updated permissions needed for accessing the following routes:
* [`GET /webhooks/{webhook.id}`](/developers/resources/webhook#get-webhook)
* [`DELETE /webhooks/{webhook.id}`](/developers/resources/webhook#delete-webhook)
* [`PATCH /webhooks/{webhook.id}`](/developers/resources/webhook#modify-webhook)
* [`GET /channels/{channel.id}/webhooks`](/developers/resources/webhook#get-channel-webhooks)
* [`POST /channels/{channel.id}/webhooks`](/developers/resources/webhook#create-webhook)
## Add Subcommand Groups and Subcommands to Message Interaction Objects
While this is a breaking change, most apps only rely on interaction responses (`INTERACTION_CREATE`), *not* message interaction objects (`MESSAGE_CREATE`). [Interaction responses](/developers/interactions/receiving-and-responding#interaction-object-interaction-data) are unaffected by this change.
#### Upcoming Changes
Starting **July 18, 2022**, the `name` field for [message interaction objects](/developers/interactions/receiving-and-responding#message-interaction-object) will now include subcommands and subcommand groups in the value (along with the existing top-level command). In the future, we recommend not relying on this message interaction field.
The format of the value will be the different command levels (if they exist), separated by spaces:
` `
The `name` field is only seen on messages that are a response to an interaction without an existing message, so interaction objects for message components don’t include this field.
#### Updating your app
Most apps only rely on interaction responses, not message interaction objects.
We don't recommend that your app relies on the `name` field for message interactions objects, but if it does you should update your app to handle subcommands and subcommand groups that your app may encounter.
As an example of the change, pretend your app had a command `/role` with subcommands `add` and `remove`. Currently, the `name` field in the original interaction payload would contain `role`. If you responded to that interaction with a message then fetched its contents, the `name` field for that message interaction object would contain `role` as well.
After this change, the `name` field for the original interaction payload will still contain `role`. However, now if you responded to that interaction with a message then fetched its contents, the `name` field for that message interaction object would contain `role add` or `role remove`.
## Min and Max Length for Command Options
Application [command options](/developers/interactions/application-commands#application-command-object-application-command-option-structure) of type `STRING` now includes optional `min_length` and `max_length` fields to control the length of text a user can input.
The value of `min_length` must be greater or equal to `0`, and the value of `max_length` must be greater or equal to `1`.
## Calculated Permissions in Interaction Payloads
Interaction payloads now contain an `app_permissions` field whose value is the computed [permissions](/developers/topics/permissions#permissions-bitwise-permission-flags) for a bot or app in the context of a specific interaction (including any channel overwrites). Similar to other permission fields, the value of `app_permissions` is a bitwise OR-ed set of permissions expressed as a string. Read details in the [interactions documentation](/developers/interactions/receiving-and-responding#interaction-object).
For apps without a bot user (or without the `bot` scope), the value of `app_permissions` will be the same as the permissions for `@everyone`, but limited to the permissions that can be used in interaction responses (currently `ATTACH_FILES`, `EMBED_LINKS`, `MENTION_EVERYONE`, and `USE_EXTERNAL_EMOJIS`).
## Changes to Bot Permissions for Interactions and Webhooks
#### Upcoming Changes
`MENTION_EVERYONE`, `SEND_TTS_MESSAGES` and `USE_EXTERNAL_EMOJIS` are the only permissions that will be affected by this change. In a previous version of this changelog, it was indicated that `ATTACH_FILES` and `EMBED_LINKS` would be affected but this is no longer the case.
Starting **August 3, 2022**, the way some of a bot's `MENTION_EVERYONE`, `SEND_TTS_MESSAGES` and `USE_EXTERNAL_EMOJIS` [permissions](/developers/topics/permissions) are calculated is changing in two cases:
* When **responding to an [interaction](/developers/interactions/receiving-and-responding)** (like application commands or message components)
* When **executing a [webhook](/developers/resources/webhook) that the bot created**
Going forward, in the above cases, a bot’s `MENTION_EVERYONE`, `SEND_TTS_MESSAGES` and `USE_EXTERNAL_EMOJIS` permissions will be calculated based on the permissions its granted, *including* any [overwrites](/developers/topics/permissions#permission-overwrites). Previously, a bot’s permissions in these cases relied only on those granted to `@everyone`.
This change *only* applies to bots. The permissions for an app without a bot user (or without the `bot` scope) will still depend on `@everyone`.
#### Updating Your App
If your bot wants to use the `MENTION_EVERYONE`, `SEND_TTS_MESSAGES` or `USE_EXTERNAL_EMOJIS` permissions when responding to interactions or executing a webhook, **ensure that the bot was installed (or explicitly granted) with them**.
Note that even if your bot is installed with certain permissions, they can be changed using overwrites. For interactions, you can use the [`app_permissions` field](/developers/interactions/receiving-and-responding#interaction-object-interaction-structure) to determine your app or bot's contextual permissions before replying.
## Message Content in Auto Moderation events
In API v10, the `MESSAGE_CONTENT` (`1 << 15`) intent is now required to receive non-empty values for the `content` and `matched_content` fields in [`AUTO_MODERATION_ACTION_EXECUTION`](/developers/events/gateway-events#auto-moderation-action-execution) gateway events. This matches the intended behavior for message content across the API.
## Updated Connection Property Field Names
The `$` prefix in [identify connection properties](/developers/events/gateway-events#identify-identify-connection-properties) are deprecated. The new field names are `os`, `browser`, and `device`. When passed, the `$`-prefixed names will resolve to the new ones.
In API v11, support for the previous field names (`$os`, `$browser`, and `$device`) will be removed.
## Auto Moderation
Add new [Auto Moderation feature](/developers/resources/auto-moderation) which enables guilds to moderate message content based on keywords, harmful links, and unwanted spam. This change includes:
* New endpoints for [creating](/developers/resources/auto-moderation#create-auto-moderation-rule), [updating](/developers/resources/auto-moderation#modify-auto-moderation-rule), and [deleting](/developers/resources/auto-moderation#delete-auto-moderation-rule) Auto Moderation rules
* New gateway events emitted when Auto Moderation rules are [created](/developers/events/gateway-events#auto-moderation-rule-create) (`AUTO_MODERATION_RULE_CREATE`), [updated](/developers/events/gateway-events#auto-moderation-rule-update) (`AUTO_MODERATION_RULE_UPDATE `), and [deleted](/developers/events/gateway-events#auto-moderation-rule-delete) (`AUTO_MODERATION_RULE_DELETE `). Requires the `AUTO_MODERATION_CONFIGURATION` (`1 << 20`) intent
* New gateway event emitted when an [action is executed](/developers/events/gateway-events#auto-moderation-action-execution) (`AUTO_MODERATION_ACTION_EXECUTION`). Requires the `AUTO_MODERATION_EXECUTION` (`1 << 21`) intent
* New [audit log entries](/developers/resources/audit-log#audit-log-entry-object-audit-log-events) when rules are created (`AUTO_MODERATION_RULE_CREATE`), updated (`AUTO_MODERATION_RULE_UPDATE`), or deleted (`AUTO_MODERATION_RULE_DELETE`), or when Auto Moderation performs an action (`AUTO_MODERATION_BLOCK_MESSAGE`)
## Updated Command Permissions
Application command permissions have been updated to add more granular control and access to commands in Discord. You can read the major changes below, and [the updated documentation](/developers/interactions/application-commands#permissions) for details.
#### Breaking changes
* Bearer tokens are now required to edit command permissions. Bearer tokens are tokens tied to an authenticating user's permissions, and can be [retrieved using OAuth](/developers/topics/oauth2). The user must have permission to manage the guild and roles.
* [`applications.commands.permissions.update`](/developers/topics/oauth2#shared-resources-oauth2-scopes) scope was added as a requirement to edit command permissions.
* Disabled the batch editing endpoint ([`PUT /applications/{application.id}/guilds/{guild.id}/commands/permissions`](/developers/interactions/application-commands#batch-edit-application-command-permissions)).
#### Other changes
* Created a [`CHANNEL` command permission type](/developers/interactions/application-commands#application-command-permissions-object-application-command-permission-type)
* Increase permission limit from `10` to `100`
* [constant (`guild_id - 1`)](/developers/interactions/application-commands#application-command-permissions-object-application-command-permissions-constants) to represent all channels in command permissions
* Added `default_member_permissions` field, which is a bitwise OR-ed set of [permissions](/developers/topics/permissions#permissions-bitwise-permission-flags), expressed as a string. This replaces the `default_permission` field, which will soon be deprecated.
* Added `dm_permission`, which is a boolean flag used to indicate whether a command is available in DMs (only for global application commands). If no value is passed, the global command will be visible in DMs.
* Added `APPLICATION_COMMAND_PERMISSIONS_UPDATE` [gateway](/developers/events/gateway-events#application-command-permissions-update) event and `APPLICATION_COMMAND_PERMISSION_UPDATE` [audit log](/developers/resources/audit-log) event.
## Forum Channels
Added new channel type, `GUILD_FORUM` (15). A `GUILD_FORUM` channel is an unreleased feature that is very similar (from an API perspective) to a `GUILD_TEXT` channel, except only threads can be created in that channel; messages cannot be sent directly in that channel. Check out the [forums topic](/developers/topics/threads#forums) for more information.
## Guild Bans Pagination
The `GET /guilds/{guild.id}/bans` endpoint has been migrated to require pagination to improve reliability and stability. Check out the [endpoint docs](/developers/resources/guild#get-guild-bans) for more information.
## API v10
* API v8 is now deprecated.
* `GET /channels/{channel.id}/threads/active` is decommissioned in favor of [`GET /guilds/{guild.id}/threads/active`](/developers/resources/guild#list-active-guild-threads).
* Starting in v10, you must specify the message content intent (`1 << 15`) to receive content-related fields in message dispatches. Read more in the [Gateway Intents documentation](/developers/events/gateway#gateway-intents).
* To specify a reason for an administrative action in audit logs, apps must now pass the `X-Audit-Log-Reason` header rather than the `reason` parameter for all endpoints. Read more in the [Audit Logs documentation](/developers/resources/audit-log).
* Message routes (like [`POST /channels/{channel.id}/messages`](/developers/resources/message#create-message)) now use the `embeds` field (an array of embed objects) instead of `embed`.
* The `summary` field for [applications](/developers/resources/application) now returns an empty string for all API versions.
* The `name` and `description` fields for [Achievements](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/docs/game_sdk/Achievements.md#achievement-struct) are now strings, and localization info is now passed in new `name_localizations` and `description_localizations` dictionaries. This change standardizes localization to match [Application Commands](/developers/interactions/application-commands#localization). Read details in the [Achievements documentation](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/docs/game_sdk/Achievements.md#achievement-struct).
* Existing attachments must be specified when [`PATCH`ing messages with new attachments](/developers/reference#editing-message-attachments). Any attachments not specified will be removed and replaced with the specified list
* Requests to v10 and higher will no longer be supported on `discordapp.com` (this does **not** affect `cdn.discordapp.com`)
#### Upcoming changes
* API v6 and v7 will be decommissioned **in early 2023**
* `MESSAGE_CONTENT` is becoming a privileged intent for verified bots in 75+ servers **on August 31, 2022**. Read details in [the FAQ](https://support-dev.discord.com/hc/en-us/articles/4404772028055-Message-Content-Privileged-Intent-FAQ) or follow [the guide](/developers/tutorials/upgrading-to-application-commands) on updating your app.
* The `summary` field for applications will be removed in the next API version (v11)
## Interaction Modals and Application Command Attachment Option Type
Interaction modals are now available, allowing applications to prompt users for further detailed input. Check out [the modal docs](/developers/interactions/receiving-and-responding#interaction-response-object-modal) for more information.
Application Commands can now add an attachment option type. See [the option type table](/developers/interactions/application-commands#application-command-object-application-command-option-type) for more information.
## Guild Member Timeouts
Add new documentation for the recently released guild member timeout feature.
## Guild Scheduled Events
* Add official support for `guild_scheduled_events` field on `Guild` resource sent with `GUILD_CREATE` event
#### Nov 18, 2021
* Breaking change for return type for `GET /guilds/{guild.id}/scheduled-events/{guild_scheduled_event.id}/users`
* Add `with_user_count` query param for `GET /guilds/{guild.id}/scheduled-events/{guild_scheduled_event.id}`
* Return additional `creator` field by default in response for `GET /guilds/{guild.id}/scheduled-events/{guild_scheduled_event.id}`
* More details and clarification for the guild scheduled events feature.
* Document support for `before` and `after` query params for `GET /guilds/{guild.id}/scheduled-events/{guild_scheduled_event.id}/users`
#### Nov 15, 2021
Add new documentation for the recently released Guild Scheduled Events feature.
## Application Command Autocomplete Interactions
Autocomplete interactions are now available, allowing application commands to provide server completed options. Check out [the autocomplete interaction docs](/developers/interactions/application-commands#autocomplete) for more information.
## Updated Thread Permissions
Thread permissions have been updated and simplified:
* "Use Public Threads" is now "Create Public Threads", which allows users to create public threads and announcement threads in a channel, even if they cannot send messages in that channel.
* "Use Private Threads" is now "Create Private Threads", which allows users to create private threads in a channel, even if they cannot send messages in that channel.
A new permission has also been added:
* "Send Messages in Threads", which allows users to send a message in a thread. The "Send Messages" permission has no effect in threads: users **must** have "Send Messages in Threads" to send a message in a thread. This allows for setups where a user can participate in a thread but cannot send a message in the parent channel (like a thread on an announcement post).
## User and Message Commands
[User commands](/developers/interactions/application-commands#user-commands) and [message commands](/developers/interactions/application-commands#message-commands) are now live! These commands appear on context menus for users and messages, with more to come in the future.
Context menu commands are a type of application command. The "Slash Commands" documentation page has been renamed to "Application Commands" and split out by type to show this.
## Select Menu Components
Select Menus are now part of the components API! They're the greatest thing since the invention of buttons yesterday. Select menus allow you to offer users a choice of one or many options in a friendly UI-based way.
Select menus can be used like other [message components](/developers/components/overview). Learn all the specifics in the [documentation](/developers/components/reference#string-select).
## Support for Multiple Embeds in Message Routes
Message routes now accept an embeds array in addition to the existing embed field. Bots can now send up to 10 embeds per message, to be consistent with webhook behavior. The existing embed field is considered deprecated and will be removed in the next API version.
## Buttons and Message Components
Message components are now available with our first two components: a layout-based `ActionRow` and...buttons!
You can now include buttons on messages sent by your app, whether they're bot messages or responses to interactions. [Learn more about message components](/developers/components/overview).
The addition of message components means new fields and response types:
* An optional `components` field has been added to the [message object](/developers/resources/message#message-object)
* New response types `6` and `7` have been added for [interaction responses](/developers/interactions/receiving-and-responding#interaction-response-object-interaction-callback-type), valid only for component-based interactions
## API v9
API v9 is now available.
API v9 includes support for [threads](/developers/topics/threads), an upcoming feature. Older API versions will not receive any Gateway Events for threads, so it is important to update soon! We've prepared a [migration guide](/developers/topics/threads) to help make the upgrade process very straightforward.
This documentation is being published early so bots can have at least two months to upgrade before threads launch.
Additionally, API v9 also removes the `/channels/:id/messages/:id/suppress-embeds` route.
## Application Command Permissions
Need to keep some of your commands safe from prying eyes, or only available to the right people? Commands now support [command permissions](/developers/interactions/application-commands#permissions)!
You can enable or disable a command (guild or global) for a specific user or role in a guild. For now, users will still be able to see the commands, but won't be able to use them.
New routes have been added to support this functionality:
* [`GET Guild Application Command Permissions`](/developers/interactions/application-commands#get-guild-application-command-permissions)
* [`GET Application Command Permissions`](/developers/interactions/application-commands#get-application-command-permissions)
* [`PUT Application Command Permissions`](/developers/interactions/application-commands#batch-edit-application-command-permissions)
A `default_permission` field has also been added to the [ApplicationCommand](/developers/interactions/application-commands#application-command-object-application-command-structure) model. This field allows you to disable commands for everyone in a guild by default, if you prefer to make some of your commands an opt-in experience.
## Large Bot Sharding Lowered to 150,000 Guilds
There have been reports that sessions have higher frequency of errors when starting if a bot has joined too many guilds (the gateway connection times out). To account for this we have lowered the requirement for large bot sharding down to 150,000 guilds in order to improve reliability.
## Changes to Slash Command Response Types and Flags
Changes to interaction response types have been made to support better designs for application commands:
* Type `2` `Acknowledge` has been deprecated
* Type `3` `ChannelMessage` has been deprecated
* Type `5` `AcknowledgeWithSource` has been renamed to `DeferredChannelMessageWithSource`
These deprecated types will be removed and break on **April 9, 2021**.
Additionally, `flags` has been documented on [InteractionApplicationCommandCallbackData](/developers/interactions/receiving-and-responding#interaction-response-object-interaction-callback-data-structure). Setting `flags` to `64` will make the interaction response ephemeral.
## Slash Commands in DMs
Slash Commands are now supported in DMs with bots. Due to this change, some of the fields on the [Interaction object](/developers/interactions/receiving-and-responding#interaction-object-interaction-structure) have been made optional. Newly optional fields don't reflect any behavior changes in Slash Commands within guilds; they are to support commands in the context of a DM only.
## Change to Permission Checking when Creating Channels
Permission overwrites in the guild channel creation endpoint are now validated against the permissions your bot has in the guild. Permission overwrites specified in the request body when creating guild channels will now require your bot to also have the permissions being applied. Setting `MANAGE_ROLES` permission in channel overwrites is only possible for guild administrators or users with `MANAGE_ROLES` as a permission overwrite in the channel.
## Slash Commands and Interactions
Slash Commands are here! There's a *lot* to cover, so go check out specific documentation under [Slash Commands](/developers/interactions/application-commands).
Slash Commands include some new features for webhooks as well:
* Webhooks can now update previously-sent messages from the same webhook using [Edit Webhook Message](/developers/resources/webhook#edit-webhook-message) and [Delete Webhook Message](/developers/resources/webhook#delete-webhook-message)
This PR also documents the `application` field on the `READY` gateway event, which is a partial [application object](/developers/resources/application#application-object) containing `id` and `flags`.
## Inline Replies
Inline Replies have been added to our documentation. They behave differently in v6 and v8, so be cautious in your implementation:
* Inline replies are type `19` in v8, but remain type `0` in v6
* You can now add a `message_reference` on message create to create a reply
* A new field `referenced_message` has been added to the [Message Object](/developers/resources/message#message-object)
* A new field `replied_user` has been added to the [Allowed Mentions Object](/developers/resources/message#allowed-mentions-object)
* [Message Create](/developers/events/gateway-events#message-create) gateway event is guaranteed to have a `referenced_message` if the message created is a reply. Otherwise, that field is not guaranteed.
## Stickers
Stickers are now documented as part of the [message](/developers/resources/message#message-object) object.
## Gateway v6 Intent Restrictions
The v6 gateway now applies the restrictions for gateway intents. This means the new chunking limitations are now in effect, regardless of intents being used. See [Request Guild Members](/developers/events/gateway-events#request-guild-members) for further details.
Additionally, if privileged intents are not enabled in the application dashboard the bot will not receive the events for those intents.
All other intents are always enabled by default unless specified otherwise by the identify payload. We have made a support article to explain some of the changes and resulting issues with more details: [Gateway Update FAQ](https://dis.gd/gwupdate)
## API and Gateway V8
We've introduced API and Gateway v8! Changes are noted throughout the documentation, and you can also read [this commit in our docs repo](https://github.com/discord/discord-api-docs/commit/545ff4a7883e5eee7ee91d19a5e5d760a0730033) for a full diff.
The changes are:
* API and Gateway v8 are now available. v6 is still the default for the time being.
* [Gateway Intents](/developers/events/gateway#gateway-intents) are now required
* Removed `guild_subscriptions` in identify in favor of [Gateway Intents](/developers/events/gateway#gateway-intents).
* All permissions have been converted to strings-serialized numbers. As such, `permissions_new`, `allow_new`, and `deny_new` have been removed
* The `game` field has been removed. If you need a direct replacement, you can instead reference the first element of `activities`
* Channel Permission Overwrite `type`s are now numbers (0 and 1) instead of strings ("role" and "member"). However due to a current technical constraint, they are string-serialized numbers in audit log `options`.
* `embed_enabled` and `embed_channel_id` have been removed. Use `widget_enabled` and `widget_channel_id` instead.
* Form body errors have been improved to include more helpful messaging on validation. [See more here](/developers/reference#error-messages)
* The `Retry-After` header value and `retry_after` body value is now based in seconds instead of milliseconds (e.g. `123` means 123 seconds)
* The `X-RateLimit-Precision` header is no longer respected. `X-RateLimit-Reset` and `X-RateLimit-Reset-After` are always returned at millisecond precision (e.g. `123.456` instead of `124`)
* Bots no longer receive [Channel Create Gateway Event](/developers/events/gateway-events#channel-create) for DMs
* `delete-message-days` is no longer available. Use `delete_message_days`.
* Removed `roles`, `premium_since`, and `nick` from [Presence Update Gateway Event](/developers/events/gateway-events#presence-update)
* Removed some [integration object](/developers/resources/guild#integration-object) fields for Discord application integrations
* Removed `include_applications` from [Get Guild Integrations](/developers/resources/guild#get-guild-integrations). Application integrations are always included.
* The following deprecated routes have been removed for better naming conventions:
Removed in favor of `/guilds//widget`:
* `/guilds//embed`
Removed in favor of `/guilds//widget.json`:
* `/servers//embed.json`
* `/servers//widget.json`
* `/guilds//embed.json`
Removed in favor of `/guilds//widget.png`:
* `/guilds//embed.png`
Removed in favor of `/channels//messages/bulk-delete`:
* `/channels//messages/bulk_delete/`
Removed in favor of `/invites//`:
* `/invite//`
## New Permission Fields
Documented `permissions_new`, `allow_new`, and `deny_new` as string-serialized permission bitfields.
## Legacy Mention Behavior Deprecation
The legacy mention behavior for bots is now removed, and granular control of mentions should use the [Allowed Mentions](/developers/resources/message#allowed-mentions-object) API moving forwards.
## New Properties on Guild Members Chunk Event
The [Guild Members Chunk](/developers/events/gateway-events#guild-members-chunk) gateway event now contains two properties: `chunk_index` and `chunk_count`. These values can be used to keep track of how many events you have left to receive in response to a [Request Guild Members](/developers/events/gateway-events#request-guild-members) command.
## New Allowed Mentions Object
We've added a way to specify mentions in a more granular form. This change also begins the start of a 60 day deprecation cycle on legacy mention behavior. Read more:
* [Allowed mentions object](/developers/resources/message#allowed-mentions-object)
## New Invite Events and Reactions Endpoint
We've added a new endpoint for deleting all reactions of a specific emoji from a message, as well as some new invite and reaction gateway events. Read more:
* [Delete All Reactions for Emoji](/developers/resources/message#delete-all-reactions-for-emoji)
* [Invite Create](/developers/events/gateway-events#invite-create)
* [Invite Delete](/developers/events/gateway-events#invite-delete)
* [Message Reaction Remove Emoji](/developers/events/gateway-events#message-reaction-remove-emoji)
## Rich Presence Spectate Approval
The [Spectate](/developers/developer-tools/game-sdk#onactivityspectate) functionality of Rich Presence no longer requires whitelisting or approval.
## Gateway Intents
We've added documentation around a brand new feature: [Gateway Intents!](/developers/events/gateway#gateway-intents) Gateway Intents are a great way to specify which events you want to receive from our gateway. Go on, save yourself some bandwidth and CPU usage.
Using Intents will change the behavior of some existing events and commands, so please refer to:
* [Guild Create](/developers/events/gateway-events#guild-create)
* [Request Guild Members](/developers/events/gateway-events#request-guild-members)
* [Guild Member Add](/developers/events/gateway-events#guild-member-add)
* [Guild Member Remove](/developers/events/gateway-events#guild-member-remove)
* [Guild Member Update](/developers/events/gateway-events#guild-member-update)
* [Presence Update](/developers/events/gateway-events#presence-update)
* [List Guild Members](/developers/resources/guild#list-guild-members)
## IP Discovery Updates
Updated our [IP discovery message](/developers/topics/voice-connections#ip-discovery). The old message is deprecated and will be removed in the future.
## GameSDK Version 2.5.6
Fixed a bug from the 2.5.5 release that caused network handshakes to fail, resulting in no networking data being sent. The networking manager and integrated lobby networking should be full operational again after updating.
## GameSDK Version 2.5.5
We've shipped some updates to the GameSDK, including support for Linux as well as the IL2CPP backend system for Unity. These changes also fixed a bug in the [`SetUserAchievement()`](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/docs/game_sdk/Achievements.md#setuserachievement) method.
Get the latest at the top of the [Getting Started](/developers/developer-tools/game-sdk#step-1-get-the-sdk) documentation. If you're looking for help interacting with the GameSDK or want to report a bug, join us on the [official Discord](https://discord.gg/discord-developers).
## Changes to Special Channels
News Channels are now changed to Announcement Channels. Developer License owners will continue to get access to them (both existing and new). Underlying channel type (GUILD\_NEWS = 5) remains the same.
## More Precise Rate Limits
You can now get more precise rate limit reset times, via a new request header. Check out the [rate limits](/developers/topics/rate-limits) documentation for more information.
## Bot Tokens for Achievements
You can now use Bot tokens for authorization headers against the HTTP API for [Achievements](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/docs/game_sdk/Achievements.md#the-api-way).
## Additional Team Information
Additional information around Teams has been added to both the API and the documentation. The [Teams](/developers/topics/teams) page now includes information about the team and team member objects. Additionally, the [Get Current Application Information](/developers/topics/oauth2#get-current-bot-application-information) endpoint now returns a `team` object if that application belongs to a team. That documentation has also been updated to includes fields that were missing for applications that are games sold on Discord.
## Added Info Around Nitro Boosting Experiment
Additional information has been documented to support [Server Nitro Boosting](https://support.discord.com/hc/en-us/articles/360028038352-Server-Boosting). This includes the addition of a few [message types](/developers/resources/message#message-object-message-types), as well as some [new fields on guilds](/developers/resources/guild#guild-object-premium-tier). Please note that this feature is currently under experimentation, and these fields may be subject to change.
## Deprecation of Discord-RPC Rich Presence SDK
The [Discord-RPC](https://github.com/discord/discord-rpc) implementation of Rich Presence has been deprecated in favor of Discord's new GameSDK. If you're interested in using Rich Presence, please read our [SDK Starter Guide](/developers/developer-tools/game-sdk#getting-started) and check out the relevant functions in the [Activity Manager](/developers/developer-tools/game-sdk#activities).
## New Invite Object Fields
The [Invite Object](/developers/resources/invite#invite-object) now includes two additional fields, `target_user` and `target_user_type`.
## Ask to Join & Rich Presence SDK
Ask to Join no longer requires approval or whitelisting to use. You are welcome to create in-game UI, but all Ask to Join requests are also now handled by the Discord overlay.
There have also been some small additions to the Rich Presence SDK. The previously undocumented `UpdateHandlers()` function is now documented.
## Documentation: Dispatch Store Listings
Dispatch documentation around store listings has been removed. Store pages for the Discord Store are now managed entirely within the [Developer Portal](https://discord.com/developers).
## Enhancement: User Object
The [User object](/developers/resources/user#user-object) now includes two new additional fields, `premium_type` and `flags`. These can be used to know the Nitro status of a user, or determine which HypeSquad house a user is in.
## Documentation Fix: List of Open DMS in Certain Payloads
The documentation has been updated to correctly note that the `private_channels` field in the [Ready](/developers/events/gateway-events#ready) should be an empty array, as well as the response from `/users/@me/channels` for a bot user. This change has been in effect for a long time, but the documentation was not updated.
## Deprecation: RPC online member count and members list
We released server changes that allow guilds to represent an incomplete state of the member list in our clients, which results in inaccurate member lists and online counts over RPC. These fields are now deprecated and will now return an empty members array and an online count of 0 moving forward.
## Enhancement: New Message Properties
Additional `activity` and `application` fields—as well as corresponding object documentation—have been added to the [Message](/developers/resources/message#message-object) object in support of our newly-released [Spotify integration](https://support.discord.com/hc/en-us/articles/360000167212-Discord-Spotify-Connection) and previous Rich Presence enhancements.
## Enhancement: Get Guild Emoji Endpoint
The [Get Guild Emoji](/developers/resources/emoji#get-guild-emoji) response now also includes a user object if the emoji was added by a user.
## Deprecation: Accept Invite Endpoint
The [Accept Invite](/developers/resources/invite) endpoint is deprecated starting today, and will be discontinued on March 23, 2018. The [Add Guild Member](/developers/resources/guild#add-guild-member) endpoint should be used in its place.
## Semi-Breaking Change: Very Large Bot Sharding
Additional sharding requirements and information for bots in over 100,000 guilds has been added. This requires a small change in numbers of shards for affected bots. See the [documentation](/developers/events/gateway#sharding-for-large-bots) for more information.
## New Feature: Rich Presence
Rich Presence is now live and available for all developers! Rich Presence allows developers to closely integrate with Discord in a number of new, cool ways like:
* Showing more information about a user's current game in their profile
* Allowing users to post invitations to join their party or spectate their game in chat
* Displaying "Spectate" and "Ask to Join" buttons on users' profiles
For more information, check out our [Rich Presence site](https://discord.com/rich-presence). To get started on development, [read the docs](/developers/rich-presence/overview)!
## Breaking Change: API & Gateway Below v6 Discontinued
[API](/developers/reference#api-versioning) and Gateway versions below v6 are now discontinued after being previously deprecated. Version 6 is now the default API and Gateway version. Attempting to use a version below 6 will result in an error.
## New Feature: Channel Categories
Changes have been made throughout the documentation to reflect the addition of channel categories to Discord. These includes an additional field—`parent_id`—to the base [channel](/developers/resources/channel#channel-object) object and a new [channel category example](/developers/resources/channel#channel-object-example-channel-category).
## New Feature: Emoji Endpoints
[Emoji endpoints](/developers/resources/emoji) have been added to the API. Bots can now manage guild emojis to their robo-hearts' content!
## Breaking Change: Presence Activity Objects
The `type` field in the [activity object](/developers/events/gateway-events#activity-object) for [Gateway Status Update](/developers/events/gateway-events#update-presence) and [Presence Update](/developers/events/gateway-events#presence-update) payloads is no longer optional when the activity object is not null.
## Breaking Change: Default Channels
After today, we are changing how default channels function. The "default" channel for a given user is now the channel with the highest position that their permissions allow them to see. New guilds will no longer have a default channel with the same id as the guild. Existing guilds will not have their #general channel id changed. It is possible, if permissions are set in such a way, that a user will not have a default channel in a guild.
We saw a use case in many servers where the previously-default #general channel was being repurposed as an announcement-only, non-writable channel for new members by using bots to clear the entire message history. Now, that channel can simply be deleted and re-created with the desired permissions. This change also allows dynamic default channels for users based on permissions.
We are also rolling out a change in conjunction that will allow Discord to remember your last-visited channel in a guild across sessions. Newly-joined users will be directed to the guild's default channel on first join; existing members will return to whichever channel they last visited.
## New Feature: Audit Logs
Audit logs are here! Well, they've been here all along, but now we've got [documentation](/developers/resources/audit-log) about them. Check it out, but remember: with great power comes great responsibility.
## Breaking Change: Version 6
* [Channel](/developers/resources/channel#channel-object) Object
* `is_private` removed
* [`type`](/developers/resources/channel#channel-object-channel-types) is now an integer
* `recipient` is now `recipients`, an array of [user](/developers/resources/user#user-object) objects
* [Message](/developers/resources/message#message-object) Object
* [`type`](/developers/resources/message#message-object-message-types) added to support system messages
* [Status Update](/developers/events/gateway-events#update-presence-gateway-presence-update-structure) Object
* `idle_since` renamed to `since`
[`Activity::AddButton`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#aab07650fcff18565eb78a1e2df46627e
[`ActivityInvite`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityInvite.html#af980f140c1459e1cd8f6ef3f3c07547c
[`Call::GetPTTReleaseDelay`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#ab8dc6b1527728fecb17f266d5b3e9e6e
[`Call::SetPTTActive`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#ac442b1d69b9256abbb188583c0c81c41
[`Client`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a91716140c699d8ef0bdf6bfd7ee0ae13
[`Client::AddLogCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#af78996cff24a40f5dc7066beed16692c
[`Client::CreateOrJoinLobbyWithMetadata`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a5c84fa76c73cf3c0bfd68794ca5595c1
[`Client::ExchangeChildToken`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a59f5d9d14f79eb318bf4d57f4e87a5c1
[`Client::GetCurrentUser`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a7edea2a3dfe9ae560d5fa5ba8663b5cc
[`Client::GetCurrentUserV2`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ae52259570ba657252d91f5580636fe5d
[`Client::GetGuildChannels`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#adba1e5a83c219a9c4f6dab1657778017
[`Client::GetLobbyMessagesWithLimit`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a0586192e85caf548b8b321f1cb21301f
[`Client::GetRelationships`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ad481849835cd570f0e03adafcf90125d
[`Client::GetRelationshipsByGroup`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a9f7898d3f3d1ec92b06c662df70746d5
[`Client::GetUserGuilds`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aac1ec02df6074ed9213ce230e6a42fe1
[`Client::GetUserMessageSummaries`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a32dafc20ff1f99b019e40bdc81f46dde
[`Client::GetUserMessagesWithLimit`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a054a758a76c5873b38a4d79651a5f26c
[`Client::JoinLinkedLobbyGuild`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a54ec764e72e168de419ac14e24e8fc60
[`Client::OpenConnectedGamesSettingsInDiscord`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a24f268f5eebe9919a3f774354eb577e0
[`Client::RegisterAuthorizeRequestCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a5f34b873e127a446c9ab549e4588ccd7
[`Client::RegisterLaunchCommand`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a024d7222931fdcb7d09c2b107642ecab
[`Client::RegisterLaunchSteamApplication`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a45b2c791c5b06f77d457dacb53dfba40
[`Client::RemoveAuthorizeRequestCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ab7e48864b0cedf3e8572a228ca401f2a
[`Client::RemoveDiscordAndGameFriend`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aa6d393a3d98ec5d06faef49a57d1a89b
[`Client::RevokeToken`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a30ccea6366efaf0b884efcdcc28a6f2d
[`Client::SetActivityInviteCreatedCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a3b4e37a222a8662506d763514774bedc
[`Client::SetAecDump`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a3a05b2cafaa546d915a5249c63f4059f
[`Client::SetEngineManagedAudioSession`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ade08897214152b9acfa79c263e77e366
[`Client::SetHttpRequestTimeout`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ae5fe2518b0b1b05ee1745ab0a79096b9
[`Client::SetRelationshipGroupsUpdatedCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#af12441ef091298f968075b7190851098
[`Client::SetSpeakerMode`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ac269ab57407e3b83e2bb2d30895e666d
[`Client::SetVoiceLogDir`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a48c6b7e8bbc2b632a935acafc6a5f7a7
[`Client::StartCallWithAudioCallbacks`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#abcaa891769f9e912bfa0e06ff7221b05
[`Client::UnmergeIntoProvisionalAccount`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a2da21ae8a3015e0e5e42c1a7226b256f
[`Client::UpdateRichPresence`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#af0a85e30f2b3d8a0b502fd23744ee58e
[`Client::UpdateToken`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a606b32cef7796f7fb91c2497bc31afc4
[`ClientCreateOptions`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientCreateOptions.html#ae655ad66ba64f443496c158307cc77b4
[`GuildChannel`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1GuildChannel.html#ac83eedfa1d9434dfa245ae39bb3a0915
[`MessageHandle`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#ae25595b43bc74b0c4c92c5165d16382f
[`MessageHandle::ModerationMetadata`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#afb9ae126f6b1f0de006f2be8e3688205
[`RelationshipHandle`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1RelationshipHandle.html#a7da36b15ad0b7d38ba658a622e9ded77
# Components Overview
Source: https://docs.discord.com/developers/components/overview
Learn about Discord's interactive message components.
Components allow you to add interactive elements to modals and the messages your app sends. They're accessible, customizable, and easy to use. For an introduction to component types and modals, see the [Components & Modals](/developers/platform/components) platform page.
To use components, messages must be sent with the `IS_COMPONENTS_V2` flag (`1<<15`). Note that using this flag disables traditional content and embeds - all content must be sent as components instead.
[Legacy message component behavior](/developers/components/reference#legacy-message-component-behavior) will **not** be deprecated and will continue to be available to your apps on a message-by-message basis. However, we recommend using the new components for new projects and features.
}>
A guide on sending Message Components with examples.
}>
A guide on sending Modal Components with examples.
}>
Explore the Components reference documentation.
***
## Get Help & Join the Community
Do you have a question or want to connect with other app developers?
* Join our [DDevs Discord Server](https://discord.gg/discord-developers) and get help from the community, share best practices, and discover new ways to enhance your apps.
# Component Reference
Source: https://docs.discord.com/developers/components/reference
This document serves as a comprehensive reference for all available components. It covers three main categories:
* **Layout Components** - For organizing and structuring content (Action Rows, Sections, Containers)
* **Content Components** - For displaying static text, images, and files (Text Display, Media Gallery, Thumbnails)
* **Interactive Components** - For user interactions (Buttons, Select Menus, Text Input)
To use these components, you need to send the [message flag](/developers/resources/message#message-object-message-flags) `1 << 15` (IS\_COMPONENTS\_V2) which can be sent on a per-message basis. Once a message has been sent with this flag, it can't be removed from that message. This enables the new components system with the following changes:
* The `content` and `embeds` fields will no longer work but you'll be able to use [Text Display](/developers/components/reference#text-display) and [Container](/developers/components/reference#container) as replacements
* Attachments won't show by default - they must be exposed through components
* The `poll` and `stickers` fields are disabled
* Messages allow up to 40 total components
[Legacy component behavior](/developers/components/reference#legacy-message-component-behavior) will continue to work but provide less flexibility and control over the message layout.
For a practical guide on implementing these components, see our [Using Message Components](/developers/components/using-message-components) and [Using Modal Components](/developers/components/using-modal-components) documentation.
***
## What is a Component
Components allow you to style and structure your messages, modals, and interactions. They are interactive elements that can create rich user experiences in your Discord applications.
Components are a field on the [message object](/developers/resources/message#message-object) and [modal](/developers/interactions/receiving-and-responding#interaction-response-object-modal). You can use them when creating messages or responding to an interaction, like an [application command](/developers/interactions/application-commands).
### Component Object
###### Component Types
The following is a complete table of available components. Details about each component are in the sections below.
| Type | Name | Description | Style | Usage |
| ---- | ------------------------------------------------------------------------- | -------------------------------------------------------------- | ----------- | -------------- |
| 1 | [Action Row](/developers/components/reference#action-row) | Container to display a row of interactive components | Layout | Message |
| 2 | [Button](/developers/components/reference#button) | Button object | Interactive | Message |
| 3 | [String Select](/developers/components/reference#string-select) | Select menu for picking from defined text options | Interactive | Message, Modal |
| 4 | [Text Input](/developers/components/reference#text-input) | Text input object | Interactive | Modal |
| 5 | [User Select](/developers/components/reference#user-select) | Select menu for users | Interactive | Message, Modal |
| 6 | [Role Select](/developers/components/reference#role-select) | Select menu for roles | Interactive | Message, Modal |
| 7 | [Mentionable Select](/developers/components/reference#mentionable-select) | Select menu for mentionables (users *and* roles) | Interactive | Message, Modal |
| 8 | [Channel Select](/developers/components/reference#channel-select) | Select menu for channels | Interactive | Message, Modal |
| 9 | [Section](/developers/components/reference#section) | Container to display text alongside an accessory component | Layout | Message |
| 10 | [Text Display](/developers/components/reference#text-display) | Markdown text | Content | Message, Modal |
| 11 | [Thumbnail](/developers/components/reference#thumbnail) | Small image that can be used as an accessory | Content | Message |
| 12 | [Media Gallery](/developers/components/reference#media-gallery) | Display images and other media | Content | Message |
| 13 | [File](/developers/components/reference#file) | Displays an attached file | Content | Message |
| 14 | [Separator](/developers/components/reference#separator) | Component to add vertical padding between other components | Layout | Message |
| 17 | [Container](/developers/components/reference#container) | Container that visually groups a set of components | Layout | Message |
| 18 | [Label](/developers/components/reference#label) | Container associating a label and description with a component | Layout | Modal |
| 19 | [File Upload](/developers/components/reference#file-upload) | Component for uploading files | Interactive | Modal |
| 21 | [Radio Group](/developers/components/reference#radio-group) | Single-choice set of options | Interactive | Modal |
| 22 | [Checkbox Group](/developers/components/reference#checkbox-group) | Multi-selectable group of checkboxes | Interactive | Modal |
| 23 | [Checkbox](/developers/components/reference#checkbox) | Single checkbox for yes/no choice | Interactive | Modal |
***
## Anatomy of a Component
All components have the following fields:
| Field | Type | Description |
| ----- | ------- | ---------------------------------------------------------------------------------------------- |
| type | integer | The [type](/developers/components/reference#component-object-component-types) of the component |
| id? | integer | 32 bit integer used as an optional identifier for component |
The `id` field is optional and is used to identify components in the response from an interaction. The `id` must be unique within the message and is generated sequentially if left empty. Generation of `id`s won't use another `id` that exists in the message if you have one defined for another component. Sending components with an `id` of `0` is allowed but will be treated as empty and replaced by the API.
###### Custom ID
Additionally, interactive components like buttons and selects must have a `custom_id` field. The developer defines this field when sending the component payload, and it is returned in the interaction payload sent when a user interacts with the component. For example, if you set `custom_id: click_me` on a button, you'll receive an interaction containing `custom_id: click_me` when a user clicks that button.
`custom_id` is only available on interactive components and must be unique per component. Multiple components on the same message must not share the same `custom_id`. This field is a string of 1 to 100 characters and can be used flexibly to maintain state or pass through other important data.
| Field | Type | Description |
| ---------- | ------ | ---------------------------------------------- |
| custom\_id | string | Developer-defined identifier, 1-100 characters |
***
## Action Row
An Action Row is a top-level layout component.
Action Rows can contain one of the following:
* Up to 5 contextually grouped [buttons](/developers/components/reference#button)
* A single select component ([string select](/developers/components/reference#string-select), [user select](/developers/components/reference#user-select), [role select](/developers/components/reference#role-select), [mentionable select](/developers/components/reference#mentionable-select), or [channel select](/developers/components/reference#channel-select))
[Label](/developers/components/reference#label) is recommended for use over an Action Row in modals. Action Row with Text Inputs in modals are now deprecated.
###### Action Row Structure
| Field | Type | Description |
| ---------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| type | integer | `1` for action row component |
| id? | integer | Optional identifier for component |
| components | array of [action row child components](/developers/components/reference#action-row-action-row-child-components) | Up to 5 interactive [button](/developers/components/reference#button) components or a single [select](/developers/components/reference#user-select) component |
###### Action Row Child Components
| Available Components | Description |
| ------------------------------------------------------------------------- | ----------------------------------------- |
| [Button](/developers/components/reference#button) | An Action Row can contain up to 5 Buttons |
| [String Select](/developers/components/reference#string-select) | A single String Select |
| [User Select](/developers/components/reference#user-select) | A single User Select |
| [Role Select](/developers/components/reference#role-select) | A single Role Select |
| [Mentionable Select](/developers/components/reference#mentionable-select) | A single Mentionable Select |
| [Channel Select](/developers/components/reference#channel-select) | A single Channel Select |
###### Examples
```jsonc theme={"system"}
{
"flags": 32768,
"components": [
{
"type": 1, // ComponentType.ACTION_ROW
"components": [
{
"type": 2, // ComponentType.BUTTON
"custom_id": "click_yes",
"label": "Accept",
"style": 1
},
{
"type": 2, // ComponentType.BUTTON
"label": "Learn More",
"style": 5,
"url": "http://watchanimeattheoffice.com/"
},
{
"type": 2, // ComponentType.BUTTON
"custom_id": "click_no",
"label": "Decline",
"style": 4
}
]
}
]
}
```
***
## Button
A Button is an interactive component that can only be used in messages. It creates clickable elements that users can interact with, sending an [interaction](/developers/interactions/receiving-and-responding#interaction-object) to your app when clicked.
Buttons must be placed inside an [Action Row](/developers/components/reference#action-row) or a [Section](/developers/components/reference#section)'s `accessory` field.
###### Button Structure
| Field | Type | Description |
| ----------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| type | integer | `2` for a button |
| id? | integer | Optional identifier for component |
| style | integer | A [button style](/developers/components/reference#button-button-styles) |
| label? | string | Text that appears on the button; max 80 characters |
| emoji? | partial [emoji](/developers/resources/emoji#emoji-object) | `name`, `id`, and `animated` |
| custom\_id? | string | Developer-defined identifier for the button; 1-100 characters |
| sku\_id? | snowflake | Identifier for a purchasable [SKU](/developers/resources/sku#sku-object), only available when using premium-style buttons |
| url? | string | URL for link-style buttons; max 512 characters |
| disabled? | boolean | Whether the button is disabled (defaults to `false`) |
Buttons come in various styles to convey different types of actions. These styles also define what fields are valid for a button.
* Non-link and non-premium buttons **must** have a `custom_id`, and cannot have a `url` or a `sku_id`.
* Link buttons **must** have a `url`, and cannot have a `custom_id`
* Link buttons do not send an [interaction](/developers/interactions/receiving-and-responding#interaction-object) to your app when clicked
* Premium buttons **must** contain a `sku_id`, and cannot have a `custom_id`, `label`, `url`, or `emoji`.
* Premium buttons do not send an [interaction](/developers/interactions/receiving-and-responding#interaction-object) to your app when clicked
###### Button Styles
| Name | Value | Action | Required Field |
| --------- | ----- | -------------------------------------------------------------- | -------------- |
| Primary | 1 | The most important or recommended action in a group of options | `custom_id` |
| Secondary | 2 | Alternative or supporting actions | `custom_id` |
| Success | 3 | Positive confirmation or completion actions | `custom_id` |
| Danger | 4 | An action with irreversible consequences | `custom_id` |
| Link | 5 | Navigates to a URL | `url` |
| Premium | 6 | Purchase | `sku_id` |
###### Examples
```jsonc theme={"system"}
{
"flags": 32768,
"components": [
{
"type": 1, // ComponentType.ACTION_ROW
"components": [
{
"type": 2, // ComponentType.BUTTON,
"custom_id": "click_me",
"label": "Click me!",
"style": 1
}
]
}
]
}
```
When a user interacts with a Button in a message, this is the basic form of the interaction data payload you will receive. The full payload
is available in the [interaction](/developers/interactions/receiving-and-responding#interaction-object-interaction-structure) reference.
```jsonc theme={"system"}
{
"type": 3, // InteractionType.MESSAGE_COMPONENT
...additionalInteractionFields, // See the Interaction documentation for all fields
"data": {
"component_type": 2, // ComponentType.BUTTON
"id": 2,
"custom_id": "click_me",
},
}
```
### Button Design Guidelines
###### General Button Content
* 34 characters max with icon or emoji.
* 38 characters max without icon or emoji.
* Keep text concise and to the point.
* Use clear and easily understandable language. Avoid jargon or overly technical terms.
* Use verbs that indicate the outcome of the action.
* Maintain consistency in language and tone across buttons.
* Anticipate the need for translation and test for expansion or contraction in different languages.
###### Multiple Buttons
Use different button styles to create a hierarchy. Use only one `Primary` button per group.
If there are multiple buttons of equal significance, use the `Secondary` button style for all buttons.
###### Premium Buttons
Premium buttons will automatically have the following:
* Shop Icon
* SKU name
* SKU price
***
## String Select
A String Select is an interactive component that allows users to select one or more provided `options`.
String Selects can be configured for both single-select and multi-select behavior. When a user finishes making their choice(s) your app receives an [interaction](/developers/interactions/receiving-and-responding#interaction-object-interaction-structure).
String Selects are available in messages and modals. They must be placed inside an [Action Row](/developers/components/reference#action-row) in messages and a [Label](/developers/components/reference#label) in modals.
###### String Select Structure
| Field | Type | Description |
| --------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| type | integer | `3` for string select |
| id? | integer | Optional identifier for component |
| custom\_id | string | ID for the select menu; 1-100 characters |
| options | array of [select options](/developers/components/reference#string-select-select-option-structure) | Specified choices in a select menu; max 25 |
| placeholder? | string | Placeholder text if nothing is selected or default; max 150 characters |
| min\_values?\* | integer | Minimum number of items that must be chosen (defaults to 1); min 0 (see note), max 25 |
| max\_values? | integer | Maximum number of items that can be chosen (defaults to 1); max 25 |
| required?\*\* | boolean | Whether the string select is required to answer in a modal (defaults to `true`) |
| disabled?\*\*\* | boolean | Whether select menu is disabled in a message (defaults to `false`) |
\* `min_values` must be either omitted or at least `1` if `required` is omitted or `true`.
\*\* The `required` field is only available for String Selects in modals. It is ignored in messages.
\*\*\* Using `disabled` in a modal will result in an error. Modals can not currently have disabled components in them.
###### Select Option Structure
| Field | Type | Description |
| ------------ | ---------------------------------------------------------------- | -------------------------------------------------------- |
| label | string | User-facing name of the option; max 100 characters |
| value | string | Dev-defined value of the option; max 100 characters |
| description? | string | Additional description of the option; max 100 characters |
| emoji? | partial [emoji](/developers/resources/emoji#emoji-object) object | `id`, `name`, and `animated` |
| default? | boolean | Will show this option as selected by default |
###### String Select Interaction Response Structure
| Field | Type | Description |
| ----------------- | ---------------- | ------------------------------------------------------------ |
| type\* | integer | `3` for a String Select |
| component\_type\* | integer | `3` for a String Select |
| id | integer | Unique identifier for the component |
| custom\_id | string | Developer-defined identifier for the input; 1-100 characters |
| values | array of strings | The text of the selected options |
\* In message interaction responses `component_type` will be returned and in modal interaction responses `type` will be returned.
###### Examples
```jsonc theme={"system"}
{
"flags": 32768,
"components": [
{
"type": 1, // ComponentType.ACTION_ROW,
"id": 1,
"components": [
{
"type": 3, // ComponentType.STRING_SELECT
"id": 2,
"custom_id": "favorite_bug",
"placeholder": "Favorite bug?",
"options": [
{
"label": "Ant",
"value": "ant",
"description": "(best option)",
"emoji": {"name": "🐜"}
},
{
"label": "Butterfly",
"value": "butterfly",
"emoji": {"name": "🦋"}
},
{
"label": "Caterpillar",
"value": "caterpillar",
"emoji": {"name": "🐛"}
}
]
}
]
}
]
}
```
When a user interacts with a StringSelect in a message, this is the basic form of the interaction data payload you will receive. The full payload
is available in the [interaction](/developers/interactions/receiving-and-responding#interaction-object-interaction-structure) reference.
```jsonc theme={"system"}
{
"type": 3, // InteractionType.MESSAGE_COMPONENT
...additionalInteractionFields, // See the Interaction documentation for all fields
"data": {
"component_type": 3, // ComponentType.STRING_SELECT
"custom_id": "favorite_bug",
"values": [
"butterfly",
]
},
}
```
```jsonc theme={"system"}
{
"type": 9, // InteractionCallbackType.MODAL
"data": {
"custom_id": "bug_modal",
"title": "Bug Survey",
"components": [
{
"type": 18, // ComponentType.LABEL
"id": 1,
"label": "Favorite bug?",
"component": {
"type": 3, // ComponentType.STRING_SELECT
"id": 2,
"custom_id": "favorite_bug",
"placeholder": "Ants are the best",
"options": [
{
"label": "Ant",
"value": "ant",
"description": "(best option)",
"emoji": {"name": "🐜"}
},
{
"label": "Butterfly",
"value": "butterfly",
"emoji": {"name": "🦋"}
},
{
"label": "Caterpillar",
"value": "caterpillar",
"emoji": {"name": "🐛"}
}
]
}
}
]
}
}
```
When a user submits a modal that contains a String Select, this is the basic form of the interaction data payload you will receive. The full payload
is available in the [interaction](/developers/interactions/receiving-and-responding#interaction-object-interaction-structure) reference.
```jsonc theme={"system"}
{
"type": 5, // InteractionType.MODAL_SUBMIT
...additionalInteractionFields, // See the Interaction documentation for all fields
"data": {
"custom_id": "bug_modal",
"components": [
{
"type": 18, // ComponentType.LABEL
"id": 1,
"component": {
"type": 3, // ComponentType.STRING_SELECT
"id": 2,
"custom_id": "favorite_bug",
"values": [
"butterfly",
]
}
}
]
},
}
```
***
## Text Input
Text Input is an interactive component that allows users to enter free-form text responses in modals. It supports both short, single-line inputs and longer, multi-line paragraph inputs.
Text Inputs can only be used within modals and must be placed inside a [Label](/developers/components/reference#label).
We no longer recommend using Text Input within an [Action Row](/developers/components/reference#action-row) in modals. Going forward all Text Inputs should be placed inside a [Label](/developers/components/reference#label) component.
###### Text Input Structure
| Field | Type | Description |
| ------------ | ------- | ------------------------------------------------------------------------------------- |
| type | integer | `4` for a text input |
| id? | integer | Optional identifier for component |
| custom\_id | string | Developer-defined identifier for the input; 1-100 characters |
| style | integer | The [Text Input Style](/developers/components/reference#text-input-text-input-styles) |
| min\_length? | integer | Minimum input length for a text input; min 0, max 4000 |
| max\_length? | integer | Maximum input length for a text input; min 1, max 4000 |
| required? | boolean | Whether this component is required to be filled (defaults to `true`) |
| value? | string | Pre-filled value for this component; max 4000 characters |
| placeholder? | string | Custom placeholder text if the input is empty; max 100 characters |
The `label` field on a Text Input is deprecated in favor of `label` and `description` on the [Label](/developers/components/reference#label) component.
###### Text Input Styles
| Name | Value | Description |
| --------- | ----- | ----------------- |
| Short | 1 | Single-line input |
| Paragraph | 2 | Multi-line input |
###### Text Input Interaction Response Structure
| Field | Type | Description |
| ---------- | ------- | ------------------------------------------------------------ |
| type | integer | `4` for a Text Input |
| id | integer | Unique identifier for the component |
| custom\_id | string | Developer-defined identifier for the input; 1-100 characters |
| value | string | The user's input text |
###### Examples
```jsonc theme={"system"}
{
"type": 9, // InteractionCallbackType.MODAL
"data": {
"custom_id": "game_feedback_modal",
"title": "Game Feedback",
"components": [
{
"type": 18, // ComponentType.LABEL
"label": "What did you find interesting about the game?",
"description": "Please give us as much detail as possible so we can improve the game!",
"component": {
"type": 4, // ComponentType.TEXT_INPUT
"custom_id": "game_feedback",
"style": 2,
"min_length": 100,
"max_length": 4000,
"placeholder": "Write your feedback here...",
"required": true
}
}
]
}
}
```
When a user submits a modal that contains a TextInput, this is the basic form of the interaction data payload you will receive. The full payload
is available in the [interaction](/developers/interactions/receiving-and-responding#interaction-object-interaction-structure) reference.
```jsonc theme={"system"}
{
"type": 5, // InteractionType.MODAL_SUBMIT
...additionalInteractionFields, // See the Interaction documentation for all fields
"data": {
"custom_id": "game_feedback_modal",
"components": [
{
"type": 18, // ComponentType.LABEL
"id": 1,
"component": {
"type": 4, // ComponentType.TEXT_INPUT
"id": 2,
"custom_id": "game_feedback",
"value": "The recent changes to acceleration feel much better, but shadows still need help"
}
}
]
},
}
```
***
## User Select
A User Select is an interactive component that allows users to select one or more users in a message or modal. Options are automatically populated based on the server's available users.
User Selects can be configured for both single-select and multi-select behavior. When a user finishes making their choice(s) your app receives an [interaction](/developers/interactions/receiving-and-responding#interaction-object-interaction-structure).
User Selects are available in messages and modals. They must be placed inside an [Action Row](/developers/components/reference#action-row) in messages and a [Label](/developers/components/reference#label) in modals.
###### User Select Structure
| Field | Type | Description |
| ---------------- | ------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| type | integer | `5` for user select |
| id? | integer | Optional identifier for component |
| custom\_id | string | ID for the select menu; 1-100 characters |
| placeholder? | string | Placeholder text if nothing is selected; max 150 characters |
| default\_values? | array of [default value objects](/developers/components/reference#user-select-select-default-value-structure) | List of default values for auto-populated select menu components; number of default values must be in the range defined by `min_values` and `max_values` |
| min\_values?\* | integer | Minimum number of items that must be chosen (defaults to 1); min 0 (see note), max 25 |
| max\_values? | integer | Maximum number of items that can be chosen (defaults to 1); max 25 |
| required?\*\* | boolean | Whether the user select is required to answer in a modal (defaults to `true`) |
| disabled?\*\*\* | boolean | Whether select menu is disabled in a message (defaults to `false`) |
\* `min_values` must be either omitted or at least `1` if `required` is omitted or `true`.
\*\* The `required` field is only available for User Selects in modals. It is ignored in messages.
\*\*\* Using `disabled` in a modal will result in an error. Modals can not currently have disabled components in them.
###### Select Default Value Structure
| Field | Type | Description |
| ----- | --------- | ----------------------------------------------------------------------------- |
| id | snowflake | ID of a user, role, or channel |
| type | string | Type of value that `id` represents. Either `"user"`, `"role"`, or `"channel"` |
###### User Select Interaction Response Structure
| Field | Type | Description |
| ----------------- | ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
| type\* | integer | `5` for a User Select |
| component\_type\* | integer | `5` for a User Select |
| id | integer | Unique identifier for the component |
| custom\_id | string | Developer-defined identifier for the input; 1-100 characters |
| resolved | [resolved data](/developers/interactions/receiving-and-responding#interaction-object-resolved-data-structure) | Resolved entities from selected options |
| values | array of snowflakes | IDs of the selected users |
\* In message interaction responses `component_type` will be returned and in modal interaction responses `type` will be returned.
###### Examples
```jsonc theme={"system"}
{
"flags": 32768,
"components": [
{
"type": 1, // ComponentType.ACTION_ROW
"components": [
{
"type": 5, // ComponentType.USER_SELECT
"custom_id": "user_select",
"placeholder": "Select a user"
}
]
}
]
}
```
When a user interacts with a User Select in a message, this is the basic form of the interaction data payload you will receive. The full payload
is available in the [interaction](/developers/interactions/receiving-and-responding#interaction-object-interaction-structure) reference.
`members` and `users` may both be present in the `resolved` object when a user is selected.
```jsonc theme={"system"}
{
"type": 3, // InteractionType.MESSAGE_COMPONENT
...additionalInteractionFields, // See the Interaction documentation for all fields
"data": {
"component_type": 5, // ComponentType.USER_SELECT
"id": 2,
"custom_id": "user_select",
"values": [
"1111111111111111111",
],
"resolved": {
"members": {
"1111111111111111111": {
"avatar": null,
"banner": null,
"collectibles": null,
"communication_disabled_until": null,
"flags": 0,
"joined_at": "2025-05-16T22:51:16.692000+00:00",
"nick": null,
"pending": false,
"permissions": "2248473465835073",
"premium_since": null,
"roles": [
"2222222222222222222"
],
"unusual_dm_activity_until": null
}
},
"users": {
"1111111111111111111": {
"avatar": "d54e87d20539fe9aad2f2cebe56809a2",
"avatar_decoration_data": null,
"bot": true,
"clan": null,
"collectibles": null,
"discriminator": "9062",
"display_name_styles": null,
"global_name": null,
"id": "1111111111111111111",
"primary_guild": null,
"public_flags": 524289,
"username": "ExampleBot"
}
}
}
},
}
```
```jsonc theme={"system"}
{
"type": 9,
"data": {
"custom_id": "user_modal",
"title": "User Chooser",
"components": [
{
"type": 18, // ComponentType.LABEL
"label": "Choose your users",
"component": {
"type": 5, // ComponentType.USER_SELECT
"custom_id": "user_selected",
"max_values": 5,
"required": true
}
}
]
}
}
```
When a user submits a modal that contains a User Select, this is the basic form of the interaction data payload you will receive. The full payload
is available in the [interaction](/developers/interactions/receiving-and-responding#interaction-object-interaction-structure) reference.
```jsonc theme={"system"}
{
"type": 5, // InteractionType.MODAL_SUBMIT
...additionalInteractionFields, // See the Interaction documentation for all fields
"data": {
"custom_id": "user_modal",
"components": [
{
"component": {
"custom_id": "user_selected",
"id": 2,
"type": 5,
"values": [
"11111111111111111"
]
},
"id": 1,
"type": 18
}
],
"resolved": {
"members": {
"11111111111111111": {
"avatar": null,
"banner": null,
"collectibles": null,
"communication_disabled_until": null,
"flags": 0,
"joined_at": "2025-04-02T23:07:21.476000+00:00",
"nick": "Ant",
"pending": false,
"permissions": "4503599627370495",
"premium_since": null,
"roles": [
"1357409927680889032"
],
"unusual_dm_activity_until": null
}
},
"users": {
"11111111111111111": {
"avatar": "a_b15bd8ee42e3c3d9a7de129fee60bc84",
"avatar_decoration_data": null,
"clan": null,
"collectibles": {
"nameplate": {
"asset": "nameplates/spell/white_mana/",
"expires_at": null,
"label": "COLLECTIBLES_SPELL_WHITE_MANA_NP_A11Y",
"palette": "bubble_gum",
"sku_id": "1379220459203072050"
}
},
"discriminator": "0",
"display_name_styles": {
"colors": [
16777215
],
"effect_id": 4,
"font_id": 3
},
"global_name": "Anthony",
"id": "11111111111111111",
"primary_guild": null,
"public_flags": 65,
"username": "actuallyanthony"
}
}
}
}
}
```
***
## Role Select
A Role Select is an interactive component that allows users to select one or more roles in a message or modal. Options are automatically populated based on the server's available roles.
Role Selects can be configured for both single-select and multi-select behavior. When a user finishes making their choice(s) your app receives an [interaction](/developers/interactions/receiving-and-responding#interaction-object-interaction-structure).
Role Selects are available in messages and modals. They must be placed inside an [Action Row](/developers/components/reference#action-row) in messages and a [Label](/developers/components/reference#label) in modals.
###### Role Select Structure
| Field | Type | Description |
| ---------------- | ------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| type | integer | `6` for role select |
| id? | integer | Optional identifier for component |
| custom\_id | string | ID for the select menu; 1-100 characters |
| placeholder? | string | Placeholder text if nothing is selected; max 150 characters |
| default\_values? | array of [default value objects](/developers/components/reference#user-select-select-default-value-structure) | List of default values for auto-populated select menu components; number of default values must be in the range defined by `min_values` and `max_values` |
| min\_values?\* | integer | Minimum number of items that must be chosen (defaults to 1); min 0 (see note), max 25 |
| max\_values? | integer | Maximum number of items that can be chosen (defaults to 1); max 25 |
| required?\*\* | boolean | Whether the role select is required to answer in a modal (defaults to `true`) |
| disabled?\*\*\* | boolean | Whether select menu is disabled in a message (defaults to `false`) |
\* `min_values` must be either omitted or at least `1` if `required` is omitted or `true`.
\*\* The `required` field is only available for Role Selects in modals. It is ignored in messages.
\*\*\* Using `disabled` in a modal will result in an error. Modals can not currently have disabled components in them.
###### Role Select Interaction Response Structure
| Field | Type | Description |
| ----------------- | ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
| type\* | integer | `6` for a Role Select |
| component\_type\* | integer | `6` for a Role Select |
| id | integer | Unique identifier for the component |
| custom\_id | string | Developer-defined identifier for the input; 1-100 characters |
| resolved | [resolved data](/developers/interactions/receiving-and-responding#interaction-object-resolved-data-structure) | Resolved entities from selected options |
| values | array of snowflakes | IDs of the selected roles |
\* In message interaction responses `component_type` will be returned and in modal interaction responses `type` will be returned.
###### Examples
```jsonc theme={"system"}
{
"flags": 32768,
"components": [
{
"type": 1, // ComponentType.ACTION_ROW
"components": [
{
"type": 6, // ComponentType.ROLE_SELECT
"custom_id": "role_ids",
"placeholder": "Which roles?",
"min_values": 1,
"max_values": 3
}
]
}
]
}
```
When a user interacts with a Role Select in a message, this is the basic form of the interaction data payload you will receive. The full payload
is available in the [interaction](/developers/interactions/receiving-and-responding#interaction-object-interaction-structure) reference.
```jsonc theme={"system"}
{
"type": 3, // InteractionType.MESSAGE_COMPONENT
...additionalInteractionFields, // See the Interaction documentation for all fields
"data": {
"component_type": 6, // ComponentType.ROLE_SELECT
"id": 2,
"custom_id": "role_ids",
"values": [
"222222222222222222",
],
"resolved": {
"roles": {
"222222222222222222": {
"color": 12745742,
"colors": {
"primary_color": 12745742,
"secondary_color": null,
"tertiary_color": null
},
"description": null,
"flags": 0,
"hoist": false,
"icon": null,
"id": "222222222222222222",
"managed": false,
"mentionable": true,
"name": "Developer",
"permissions": "0",
"position": 2,
"unicode_emoji": "🔧"
}
}
}
},
}
```
```jsonc theme={"system"}
{
"type": 9,
"data": {
"custom_id": "role_modal",
"title": "Role Select",
"components": [
{
"type": 18,
"label": "Select which roles to assign",
"component": {
"type": 6,
"custom_id": "roles_selected",
"max_values": 10,
"required": true
}
}
]
}
}
```
When a user submits a modal that contains a Role Select, this is the basic form of the interaction data payload you will receive. The full payload
is available in the [interaction](/developers/interactions/receiving-and-responding#interaction-object-interaction-structure) reference.
```jsonc theme={"system"}
{
"type": 5, // InteractionType.MODAL_SUBMIT
...additionalInteractionFields, // See the Interaction documentation for all fields
"data": {
"custom_id": "role_modal",
"components": [
{
"component": {
"custom_id": "roles_selected",
"id": 2,
"type": 6,
"values": [
"1362213912946147499",
"1357409927680889032"
]
},
"id": 1,
"type": 18
}
],
"resolved": {
"roles": {
"1357409927680889032": {
"color": 7419530,
"colors": {
"primary_color": 7419530,
"secondary_color": null,
"tertiary_color": null
},
"description": null,
"flags": 0,
"hoist": true,
"icon": null,
"id": "1357409927680889032",
"managed": false,
"mentionable": true,
"name": "Player",
"permissions": "2249596494938111",
"position": 3,
"unicode_emoji": "🎮"
},
"1362213912946147499": {
"color": 11342935,
"colors": {
"primary_color": 11342935,
"secondary_color": null,
"tertiary_color": null
},
"description": null,
"flags": 0,
"hoist": false,
"icon": null,
"id": "1362213912946147499",
"managed": false,
"mentionable": false,
"name": "Mod",
"permissions": "0",
"position": 1,
"unicode_emoji": "🔨"
}
}
}
}
}
```
***
## Mentionable Select
A Mentionable Select is an interactive component that allows users to select one or more mentionables in a message or modal. Options are automatically populated based on available mentionables in the server.
Mentionable Selects can be configured for both single-select and multi-select behavior. When a user finishes making their choice(s), your app receives an [interaction](/developers/interactions/receiving-and-responding#interaction-object-interaction-structure).
Mentionable Selects are available in messages and modals. They must be placed inside an [Action Row](/developers/components/reference#action-row) in messages and a [Label](/developers/components/reference#label) in modals.
###### Mentionable Select Structure
| Field | Type | Description |
| ---------------- | ------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| type | integer | `7` for mentionable select |
| id? | integer | Optional identifier for component |
| custom\_id | string | ID for the select menu; 1-100 characters |
| placeholder? | string | Placeholder text if nothing is selected; max 150 characters |
| default\_values? | array of [default value objects](/developers/components/reference#user-select-select-default-value-structure) | List of default values for auto-populated select menu components; number of default values must be in the range defined by `min_values` and `max_values` |
| min\_values?\* | integer | Minimum number of items that must be chosen (defaults to 1); min 0 (see note), max 25 |
| max\_values? | integer | Maximum number of items that can be chosen (defaults to 1); max 25 |
| required?\*\* | boolean | Whether the mentionable select is required to answer in a modal (defaults to `true`) |
| disabled?\*\*\* | boolean | Whether select menu is disabled in a message (defaults to `false`) |
\* `min_values` must be either omitted or at least `1` if `required` is omitted or `true`.
\*\* The `required` field is only available for Mentionable Selects in modals. It is ignored in messages.
\*\*\* Using `disabled` in a modal will result in an error. Modals can not currently have disabled components in them.
###### Mentionable Select Interaction Response Structure
| Field | Type | Description |
| ----------------- | ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
| type\* | integer | `7` for a Mentionable Select |
| component\_type\* | integer | `7` for a Mentionable Select |
| id | integer | Unique identifier for the component |
| custom\_id | string | Developer-defined identifier for the input; 1-100 characters |
| resolved | [resolved data](/developers/interactions/receiving-and-responding#interaction-object-resolved-data-structure) | Resolved entities from selected options |
| values | array of snowflakes | IDs of the selected mentionables |
\* In message interaction responses `component_type` will be returned and in modal interaction responses `type` will be returned.
###### Examples
```jsonc theme={"system"}
{
"flags": 32768,
"components": [
{
"type": 1, // ComponentType.ACTION_ROW
"components": [
{
"type": 7, // ComponentType.MENTIONABLE_SELECT
"custom_id": "who_to_ping",
"placeholder": "Who?",
}
]
}
]
}
```
When a user interacts with a Mentionable Select in a message, this is the basic form of the interaction data payload you will receive. The full payload
is available in the [interaction](/developers/interactions/receiving-and-responding#interaction-object-interaction-structure) reference.
`members` and `users` may both be present in the `resolved` object when a user is selected.
```jsonc theme={"system"}
{
"type": 3, // InteractionType.MESSAGE_COMPONENT
...additionalInteractionFields, // See the Interaction documentation for all fields
"data": {
"component_type": 7, // ComponentType.MENTIONABLE_SELECT
"id": 2,
"custom_id": "who_to_ping",
"values": [
"111111111111111111",
"222222222222222222",
],
"resolved": {
"members": {
"1111111111111111111": {
"avatar": null,
"banner": null,
"collectibles": null,
"communication_disabled_until": null,
"flags": 0,
"joined_at": "2025-05-16T22:51:16.692000+00:00",
"nick": null,
"pending": false,
"permissions": "2248473465835073",
"premium_since": null,
"roles": [
"2222222222222222222"
],
"unusual_dm_activity_until": null
}
},
"users": {
"1111111111111111111": {
"avatar": "d54e87d20539fe9aad2f2cebe56809a2",
"avatar_decoration_data": null,
"bot": true,
"clan": null,
"collectibles": null,
"discriminator": "9062",
"display_name_styles": null,
"global_name": null,
"id": "1111111111111111111",
"primary_guild": null,
"public_flags": 524289,
"username": "ExampleBot"
}
},
"roles": {
"222222222222222222": {
"color": 12745742,
"colors": {
"primary_color": 12745742,
"secondary_color": null,
"tertiary_color": null
},
"description": null,
"flags": 0,
"hoist": false,
"icon": null,
"id": "222222222222222222",
"managed": false,
"mentionable": true,
"name": "Developer",
"permissions": "0",
"position": 2,
"unicode_emoji": "🔧"
}
}
}
},
}
```
```jsonc theme={"system"}
{
"type": 9,
"data": {
"custom_id": "mentionable_modal",
"title": "Unmentionables",
"components": [
{
"type": 18,
"label": "Who gets mentioned?",
"component": {
"type": 7,
"custom_id": "mentionables_selected",
"required": true
}
}
]
}
}
```
When a user submits a modal that contains a Mentionable Select, this is the basic form of the interaction data payload you will receive. The full payload
is available in the [interaction](/developers/interactions/receiving-and-responding#interaction-object-interaction-structure) reference.
```jsonc theme={"system"}
{
"type": 5, // InteractionType.MODAL_SUBMIT
...additionalInteractionFields, // See the Interaction documentation for all fields
"data": {
"custom_id": "mentionable_modal",
"components": [
{
"component": {
"custom_id": "mentionables_selected",
"id": 2,
"type": 7,
"values": [
"1361539726405926952"
]
},
"id": 1,
"type": 18
}
],
"resolved": {
"roles": {
"1361539726405926952": {
"color": 12745742,
"colors": {
"primary_color": 12745742,
"secondary_color": null,
"tertiary_color": null
},
"description": null,
"flags": 0,
"hoist": false,
"icon": null,
"id": "1361539726405926952",
"managed": false,
"mentionable": true,
"name": "Developer",
"permissions": "0",
"position": 2,
"unicode_emoji": "🔧"
}
}
}
}
}
```
***
## Channel Select
A Channel Select is an interactive component that allows users to select one or more channels in a message or modal. Options are automatically populated based on available channels in the server and can be filtered by channel types.
Channel Selects can be configured for both single-select and multi-select behavior. When a user finishes making their choice(s) your app receives an [interaction](/developers/interactions/receiving-and-responding#interaction-object-interaction-structure).
Channel Selects are available in messages and modals. They must be placed inside an [Action Row](/developers/components/reference#action-row) in messages and a [Label](/developers/components/reference#label) in modals.
###### Channel Select Structure
| Field | Type | Description |
| ---------------- | ------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| type | integer | `8` for channel select |
| id? | integer | Optional identifier for component |
| custom\_id | string | ID for the select menu; 1-100 characters |
| channel\_types? | array of [channel types](/developers/resources/channel#channel-object-channel-types) | List of channel types to include in the channel select component |
| placeholder? | string | Placeholder text if nothing is selected; max 150 characters |
| default\_values? | array of [default value objects](/developers/components/reference#user-select-select-default-value-structure) | List of default values for auto-populated select menu components; number of default values must be in the range defined by `min_values` and `max_values` |
| min\_values?\* | integer | Minimum number of items that must be chosen (defaults to 1); min 0 (see note), max 25 |
| max\_values? | integer | Maximum number of items that can be chosen (defaults to 1); max 25 |
| required?\*\* | boolean | Whether the channel select is required to answer in a modal (defaults to `true`) |
| disabled?\*\*\* | boolean | Whether select menu is disabled in a message (defaults to `false`) |
\* `min_values` must be either omitted or at least `1` if `required` is omitted or `true`.
\*\* The `required` field is only available for Channel Selects in modals. It is ignored in messages.
\*\*\* Using `disabled` in a modal will result in an error. Modals can not currently have disabled components in them.
###### Channel Select Interaction Response Structure
| Field | Type | Description |
| ----------------- | ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
| type\* | integer | `8` for a Channel Select |
| component\_type\* | integer | `8` for a Channel Select |
| id | integer | Unique identifier for the component |
| custom\_id | string | Developer-defined identifier for the input; 1-100 characters |
| resolved | [resolved data](/developers/interactions/receiving-and-responding#interaction-object-resolved-data-structure) | Resolved entities from selected options |
| values | array of snowflakes | IDs of the selected channels |
\* In message interaction responses `component_type` will be returned and in modal interaction responses `type` will be returned.
###### Examples
```jsonc theme={"system"}
{
"flags": 32768,
"components": [
{
"type": 1, // ComponentType.ACTION_ROW
"components": [
{
"type": 8, // ComponentType.CHANNEL_SELECT
"custom_id": "notification_channel",
"channel_types": [0], // ChannelType.TEXT
"placeholder": "Which text channel?"
}
]
}
]
}
```
When a user interacts with a Channel Select in a message, this is the basic form of the interaction data payload you will receive. The full payload
is available in the [interaction](/developers/interactions/receiving-and-responding#interaction-object-interaction-structure) reference.
```jsonc theme={"system"}
{
"type": 3, // InteractionType.MESSAGE_COMPONENT
...additionalInteractionFields, // See the Interaction documentation for all fields
"data": {
"component_type": 8, // ComponentType.CHANNEL_SELECT
"id": 2,
"custom_id": "notification_channel",
"values": [
"333333333333333333",
],
"resolved": {
"channels": {
"333333333333333333": {
"flags": 0,
"guild_id": "44444444444444444",
"id": "333333333333333333",
"last_message_id": null,
"name": "playtesting",
"nsfw": false,
"parent_id": "5555555555555555",
"permissions": "4503599627370495",
"position": 1,
"rate_limit_per_user": 0,
"topic": null,
"type": 0 // ChannelType.TEXT
}
}
}
},
}
```
```jsonc theme={"system"}
{
"type": 9,
"data": {
"custom_id": "channel_modal",
"title": "Lockdown",
"components": [
{
"type": 18,
"label": "Which channel should be locked?",
"component": {
"type": 8,
"custom_id": "channel_selected",
"required": true
}
}
]
}
}
```
When a user submits a modal that contains a Channel Select, this is the basic form of the interaction data payload you will receive. The full payload
is available in the [interaction](/developers/interactions/receiving-and-responding#interaction-object-interaction-structure) reference.
```jsonc theme={"system"}
{
"type": 5, // InteractionType.MODAL_SUBMIT
...additionalInteractionFields, // See the Interaction documentation for all fields
"data": {
"custom_id": "channel_modal",
"components": [
{
"component": {
"custom_id": "channel_selected",
"id": 2,
"type": 8,
"values": [
"1357483683627663450"
]
},
"id": 1,
"type": 18
}
],
"resolved": {
"channels": {
"1357483683627663450": {
"flags": 0,
"guild_id": "1111111111111111",
"id": "1357483683627663450",
"last_message_id": null,
"name": "playtesting",
"nsfw": false,
"parent_id": "1357129309164404938",
"permissions": "4503599627370495",
"position": 1,
"rate_limit_per_user": 0,
"topic": null,
"type": 0
}
}
}
}
}
```
***
## Section
A Section is a top-level layout component that allows you to contextually associate content with an accessory component.
The typical use-case is to contextually associate [text content](/developers/components/reference#text-display) with
an [accessory](/developers/components/reference#section-section-accessory-components).
Sections are currently only available in messages.
To use this component in messages you must send the [message flag](/developers/resources/message#message-object-message-flags) `1 << 15` (IS\_COMPONENTS\_V2) which can be activated on a per-message basis.
###### Section Structure
| Field | Type | Description |
| ---------- | ------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------- |
| type | integer | `9` for section component |
| id? | integer | Optional identifier for component |
| components | array of [section child components](/developers/components/reference#section-section-child-components) | One to three child components representing the content of the section that is contextually associated to the accessory |
| accessory | [section accessory component](/developers/components/reference#section-section-accessory-components) | A component that is contextually associated to the content of the section |
Don't hardcode `components` to contain only text components. We may add other components in the future. Similarly, `accessory` may be expanded to include other components in the future.
###### Section Child Components
| Available Components |
| ------------------------------------------------------------- |
| [Text Display](/developers/components/reference#text-display) |
###### Section Accessory Components
| Available Components |
| ------------------------------------------------------- |
| [Button](/developers/components/reference#button) |
| [Thumbnail](/developers/components/reference#thumbnail) |
###### Examples
```jsonc theme={"system"}
{
"flags": 32768,
"components": [
{
"type": 9, // ComponentType.SECTION
"components": [
{
"type": 10, // ComponentType.TEXT_DISPLAY
"content": "# Real Game v7.3"
},
{
"type": 10, // ComponentType.TEXT_DISPLAY
"content": "Hope you're excited, the update is finally here! Here are some of the changes:\n- Fixed a bug where certain treasure chests wouldn't open properly\n- Improved server stability during peak hours\n- Added a new type of gravity that will randomly apply when the moon is visible in-game\n- Every third thursday the furniture will scream your darkest secrets to nearby npcs"
},
{
"type": 10, // ComponentType.TEXT_DISPLAY
"content": "-# That last one wasn't real, but don't use voice chat near furniture just in case..."
}
],
"accessory": {
"type": 11, // ComponentType.THUMBNAIL
"media": {
"url": "https://websitewithopensourceimages/gamepreview.webp"
}
}
}
]
}
```
***
## Text Display
A Text Display is a content component that allows you to add markdown formatted text, including mentions (users, roles, etc) and emojis.
The behavior of this component is extremely similar to the [`content` field of a message](/developers/resources/message#message-object), but allows you to add multiple text components, controlling the layout of your message.
When sent in a message, pingable mentions (@user, @role, etc) present in this component will ping and send notifications based on the
value of the [allowed mention object](/developers/resources/message#allowed-mentions-object) set in [`message.allowed_mentions`](/developers/resources/message#message-object).
To use this component in messages you must send the [message flag](/developers/resources/message#message-object-message-flags) `1 << 15` (IS\_COMPONENTS\_V2) which can be activated on a per-message basis.
###### Text Display Structure
| Field | Type | Description |
| ------- | ------- | ------------------------------------------------ |
| type | integer | `10` for text display |
| id? | integer | Optional identifier for component |
| content | string | Text that will be displayed similar to a message |
###### Text Display Interaction Response Structure
| Field | Type | Description |
| ----- | ------- | ----------------------------------- |
| type | integer | `10` for a Text Display |
| id | integer | Unique identifier for the component |
###### Examples
```jsonc theme={"system"}
{
"flags": 32768,
"components": [
{
"type": 10, // ComponentType.TEXT_DISPLAY
"content": "# This is a Text Display\nAll the regular markdown rules apply\n- You can make lists\n- You can use `code blocks`\n- You can use [links](http://watchanimeattheoffice.com/)\n- Even :blush: :star_struck: :exploding_head:\n- Spoiler alert: ||these too!||"
}
]
}
```
```jsonc theme={"system"}
{
"type": 9,
"data": {
"custom_id": "jail_modal",
"title": "Jail",
"components": [
{
"type": 10,
"content": "This action will move the selected user to the selected voice channel and take away all their permissions **for 1 hour**."
},
{
"type": 18,
"label": "Choose a user",
"component": {
"type": 5,
"custom_id": "user_selected",
"required": true
}
},
{
"type": 18,
"label": "Where should they be sent?",
"component": {
"type": 8,
"custom_id": "channel_selected",
"channel_types": [
2
],
"required": true
}
}
]
}
}
```
***
## Thumbnail
A Thumbnail is a content component that displays visual media in a small form-factor. It is intended as an accessory for
to other content, and is primarily usable with [sections](/developers/components/reference#section). The media displayed is
defined by the [unfurled media item](/developers/components/reference#unfurled-media-item) structure, which supports
both uploaded media and externally hosted media.
Thumbnails are currently only available in messages as an accessory in a [section](/developers/components/reference#section).
Thumbnails currently only support images, including animated formats like GIF and WEBP. Videos are not supported at this time.
To use this component, you need to send the [message flag](/developers/resources/message#message-object-message-flags) `1 << 15` (IS\_COMPONENTS\_V2), which can be activated on a per-message basis.
###### Thumbnail Structure
| Field | Type | Description |
| ------------ | --------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| type | integer | `11` for thumbnail component |
| id? | integer | Optional identifier for component |
| media | [unfurled media item](/developers/components/reference#unfurled-media-item) | A url or attachment provided as an [unfurled media item](/developers/components/reference#unfurled-media-item) |
| description? | ?string | Alt text for the media, max 1024 characters |
| spoiler? | boolean | Whether the thumbnail should be a spoiler (or blurred out). Defaults to `false` |
###### Examples
```jsonc theme={"system"}
{
"flags": 32768,
"components": [
{
"type": 9, // ComponentType.SECTION
"components": [
{
"type": 10, // ComponentType.TEXT_DISPLAY
"content": "# Real Game v7.3"
},
{
"type": 10, // ComponentType.TEXT_DISPLAY
"content": "Hope you're excited, the update is finally here! Here are some of the changes:\n- Fixed a bug where certain treasure chests wouldn't open properly\n- Improved server stability during peak hours\n- Added a new type of gravity that will randomly apply when the moon is visible in-game\n- Every third thursday the furniture will scream your darkest secrets to nearby npcs"
},
{
"type": 10, // ComponentType.TEXT_DISPLAY
"content": "-# That last one wasn't real, but don't use voice chat near furniture just in case..."
}
],
"accessory": {
"type": 11, // ComponentType.THUMBNAIL
"media": {
"url": "https://websitewithopensourceimages/gamepreview.webp"
}
}
}
]
}
```
***
## Media Gallery
A Media Gallery is a top-level content component that allows you to display 1-10 media attachments in an organized gallery format.
Each item can have optional descriptions and can be marked as spoilers.
Media Galleries are currently only available in messages.
To use this component in messages you must send the [message flag](/developers/resources/message#message-object-message-flags) `1 << 15` (IS\_COMPONENTS\_V2) which can be activated on a per-message basis.
###### Media Gallery Structure
| Field | Type | Description |
| ----- | ----------------------------------------------------------------------------------------------------------- | --------------------------------- |
| type | integer | `12` for media gallery component |
| id? | integer | Optional identifier for component |
| items | array of [media gallery items](/developers/components/reference#media-gallery-media-gallery-item-structure) | 1 to 10 media gallery items |
###### Media Gallery Item Structure
| Field | Type | Description |
| ------------ | --------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| media | [unfurled media item](/developers/components/reference#unfurled-media-item) | A url or attachment provided as an [unfurled media item](/developers/components/reference#unfurled-media-item) |
| description? | ?string | Alt text for the media, max 1024 characters |
| spoiler? | boolean | Whether the media should be a spoiler (or blurred out). Defaults to `false` |
###### Examples
```jsonc theme={"system"}
{
"flags": 32768,
"components": [
{
"type": 10, // ComponentType.TEXT_DISPLAY
"content": "Live webcam shots as of 18-04-2025 at 12:00 UTC"
},
{
"type": 12, // ComponentType.MEDIA_GALLERY
"items": [
{
"media": {"url": "https://livevideofeedconvertedtoimage/webcam1.webp"},
"description": "An aerial view looking down on older industrial complex buildings. The main building is white with many windows and pipes running up the walls."
},
{
"media": {"url": "https://livevideofeedconvertedtoimage/webcam2.webp"},
"description": "An aerial view of old broken buildings. Nature has begun to take root in the rooftops. A portion of the middle building's roof has collapsed inward. In the distant haze you can make out a far away city."
},
{
"media": {"url": "https://livevideofeedconvertedtoimage/webcam3.webp"},
"description": "A street view of a downtown city. Prominently in photo are skyscrapers and a domed building"
}
]
}
]
}
```
***
## File
A File is a top-level content component that allows you to display an [uploaded file](/developers/components/reference#uploading-a-file) as
an attachment to the message and reference it in the component. Each file component can only display 1 attached file, but you can upload
multiple files and add them to different file components within your payload.
Files are currently only available in messages.
The File component only supports using the `attachment://` protocol in [unfurled media item](/developers/components/reference#unfurled-media-item)
To use this component in messages you must send the [message flag](/developers/resources/message#message-object-message-flags) `1 << 15` (IS\_COMPONENTS\_V2) which can be activated on a per-message basis.
###### File Structure
| Field | Type | Description |
| -------- | --------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| type | integer | `13` for a file component |
| id? | integer | Optional identifier for component |
| file | [unfurled media item](/developers/components/reference#unfurled-media-item) | This unfurled media item is unique in that it **only** supports attachment references using the `attachment://` syntax |
| spoiler? | boolean | Whether the media should be a spoiler (or blurred out). Defaults to `false` |
| name? | string | The name of the file. This field is ignored and provided by the API as part of the response |
| size? | integer | The size of the file in bytes. This field is ignored and provided by the API as part of the response |
###### Examples
This example makes use of the `attachment://` protocol functionality in [unfurled media item](/developers/components/reference#unfurled-media-item).
```jsonc theme={"system"}
{
"flags": 32768,
"components": [
{
"type": 10, // ComponentType.TEXT_DISPLAY
"content": "# New game version released for testing!\nGrab the game here:"
},
{
"type": 13, // ComponentType.FILE
"file": {
"url": "attachment://game.zip"
}
},
{
"type": 10, // ComponentType.TEXT_DISPLAY
"content": "Latest manual artwork here:"
},
{
"type": 13, // ComponentType.FILE
"file": {
"url": "attachment://manual.pdf"
}
}
]
}
```
***
## Separator
A Separator is a top-level layout component that adds vertical padding and visual division between other components.
Separators are currently only available in messages.
To use this component in messages you must send the [message flag](/developers/resources/message#message-object-message-flags) `1 << 15` (IS\_COMPONENTS\_V2) which can be activated on a per-message basis.
###### Separator Structure
| Field | Type | Description |
| -------- | ------- | --------------------------------------------------------------------------------------- |
| type | integer | `14` for separator component |
| id? | integer | Optional identifier for component |
| divider? | boolean | Whether a visual divider should be displayed in the component. Defaults to `true` |
| spacing? | integer | Size of separator padding—`1` for small padding, `2` for large padding. Defaults to `1` |
###### Examples
```jsonc theme={"system"}
{
"flags": 32768,
"components": [
{
"type": 10, // ComponentType.TEXT_DISPLAY
"content": "It's dangerous to go alone!"
},
{
"type": 14, // ComponentType.SEPARATOR
"divider": true,
"spacing": 1
},
{
"type": 10, // ComponentType.TEXT_DISPLAY
"content": "Take this."
}
]
}
```
***
## Container
A Container is a top-level layout component. Containers offer the ability to visually encapsulate a collection of components
and have an optional customizable accent color bar.
Containers are currently only available in messages.
To use this component in messages you must send the [message flag](/developers/resources/message#message-object-message-flags) `1 << 15` (IS\_COMPONENTS\_V2) which can be activated on a per-message basis.
###### Container Structure
| Field | Type | Description |
| -------------- | ------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------- |
| type | integer | `17` for container component |
| id? | integer | Optional identifier for component |
| components | array of [container child components](/developers/components/reference#container-container-child-components) | Child components that are encapsulated within the Container |
| accent\_color? | ?integer | Color for the accent on the container as RGB from `0x000000` to `0xFFFFFF` |
| spoiler? | boolean | Whether the container should be a spoiler (or blurred out). Defaults to `false`. |
###### Container Child Components
| Available Components |
| --------------------------------------------------------------- |
| [Action Row](/developers/components/reference#action-row) |
| [Text Display](/developers/components/reference#text-display) |
| [Section](/developers/components/reference#section) |
| [Media Gallery](/developers/components/reference#media-gallery) |
| [Separator](/developers/components/reference#separator) |
| [File](/developers/components/reference#file) |
###### Examples
```jsonc theme={"system"}
{
"flags": 32768,
"components": [
{
"type": 17, // ComponentType.CONTAINER
"accent_color": 703487,
"components": [
{
"type": 10, // ComponentType.TEXT_DISPLAY
"content": "# You have encountered a wild coyote!"
},
{
"type": 12, // ComponentType.MEDIA_GALLERY
"items": [
{
"media": {"url": "https://websitewithopensourceimages/coyote.webp"},
}
]
},
{
"type": 10, // ComponentType.TEXT_DISPLAY
"content": "What would you like to do?"
},
{
"type": 1, // ComponentType.ACTION_ROW
"components": [
{
"type": 2, // ComponentType.BUTTON
"custom_id": "pet_coyote",
"label": "Pet it!",
"style": 1
},
{
"type": 2, // ComponentType.BUTTON
"custom_id": "feed_coyote",
"label": "Attempt to feed it",
"style": 2
},
{
"type": 2, // ComponentType.BUTTON
"custom_id": "run_away",
"label": "Run away!",
"style": 4
}
]
}
]
}
]
}
```
***
## Label
A Label is a top-level layout component. Labels wrap modal components with text as a label and optional description.
The `description` may display above or below the `component` depending on the platform.
###### Label Structure
| Field | Type | Description |
| ------------ | -------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
| type | integer | `18` for a label |
| id? | integer | Optional identifier for component |
| label | string | The label text; max 45 characters |
| description? | string | An optional description text for the label; max 100 characters |
| component | [label child component](/developers/components/reference#label-label-child-components) | The component within the label |
###### Label Child Components
| Available Components |
| ------------------------------------------------------------------------- |
| [Text Input](/developers/components/reference#text-input) |
| [String Select](/developers/components/reference#string-select) |
| [User Select](/developers/components/reference#user-select) |
| [Role Select](/developers/components/reference#role-select) |
| [Mentionable Select](/developers/components/reference#mentionable-select) |
| [Channel Select](/developers/components/reference#channel-select) |
| [File Upload](/developers/components/reference#file-upload) |
| [Radio Group](/developers/components/reference#radio-group) |
| [Checkbox Group](/developers/components/reference#checkbox-group) |
| [Checkbox](/developers/components/reference#checkbox) |
###### Label Interaction Response Structure
| Field | Type | Description |
| --------- | -------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- |
| type | integer | `18` for a Label |
| id | integer | Unique identifier for the component |
| component | [label interaction response child component](/developers/components/reference#label-label-interaction-response-child-components) | The component within the label |
###### Label Interaction Response Child Components
| Available Components |
| --------------------------------------------------------------------------------------------------------------------------- |
| [Text Input](/developers/components/reference#text-input-text-input-interaction-response-structure) |
| [String Select](/developers/components/reference#string-select-string-select-interaction-response-structure) |
| [User Select](/developers/components/reference#user-select-user-select-interaction-response-structure) |
| [Role Select](/developers/components/reference#role-select-role-select-interaction-response-structure) |
| [Mentionable Select](/developers/components/reference#mentionable-select-mentionable-select-interaction-response-structure) |
| [Channel Select](/developers/components/reference#channel-select-channel-select-interaction-response-structure) |
| [File Upload](/developers/components/reference#file-upload-file-upload-interaction-response-structure) |
| [Radio Group](/developers/components/reference#radio-group-interaction-response-structure) |
| [Checkbox Group](/developers/components/reference#checkbox-group-interaction-response-structure) |
| [Checkbox](/developers/components/reference#checkbox-interaction-response-structure) |
###### Examples
```jsonc theme={"system"}
{
"type": 9, // InteractionCallbackType.MODAL
"data": {
"custom_id": "game_feedback_modal",
"title": "Game Feedback",
"components": [
{
"type": 18, // ComponentType.LABEL
"label": "What did you find interesting about the game?",
"description": "Please give us as much detail as possible so we can improve the game!",
"component": {
"type": 4, // ComponentType.TEXT_INPUT
"custom_id": "game_feedback",
"style": 2,
"min_length": 100,
"max_length": 4000,
"placeholder": "Write your feedback here...",
"required": true
}
}
]
}
}
```
***
## File Upload
File Upload is an interactive component that allows users to upload files in modals. File Uploads can be configured to have a minimum and maximum number of files between 0 and 10, along with `required` for if the upload is required to submit the modal. The max file size a user can upload is based on the user's upload limit in that channel.
File Uploads are available on modals. They must be placed inside a [Label](/developers/components/reference#label).
###### File Upload Structure
| Field | Type | Description |
| -------------- | ------- | ------------------------------------------------------------------------------------------------------ |
| type | integer | `19` for file upload |
| id? | integer | Optional identifier for component |
| custom\_id | string | ID for the file upload; 1-100 characters |
| min\_values?\* | integer | Minimum number of items that must be uploaded (defaults to 1); min 0 (see note), max 10 |
| max\_values? | integer | Maximum number of items that can be uploaded (defaults to 1); max 10 |
| required? | boolean | Whether the file upload requires files to be uploaded before submitting the modal (defaults to `true`) |
\* `min_values` must be either omitted or at least `1` if `required` is omitted or `true`.
###### File Upload Interaction Response Structure
| Field | Type | Description |
| ---------- | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| type | integer | `19` for a File Upload |
| id | integer | Unique identifier for the component |
| custom\_id | string | Developer-defined identifier for the input; 1-100 characters |
| values | array of snowflakes | IDs of the uploaded files found in the [resolved data](/developers/interactions/receiving-and-responding#interaction-object-resolved-data-structure) |
###### Examples
```jsonc theme={"system"}
{
"type": 9,
"data": {
"custom_id": "bug_submit_modal",
"title": "Bug Submission",
"components": [
{
"type": 18, // ComponentType.LABEL
"label": "File Upload",
"description": "Please upload a screenshot or other image that shows the bug you encountered.",
"component": {
"type": 19, // ComponentType.FILE_UPLOAD
"custom_id": "file_upload",
"min_values": 1,
"max_values": 10,
"required": true
}
}
]
}
}
```
When a user submits a modal that contains a File Upload, this is the basic form of the interaction data payload you will receive. The full payload
is available in the [interaction](/developers/interactions/receiving-and-responding#interaction-object-interaction-structure) reference.
```jsonc theme={"system"}
{
"type": 5, // InteractionType.MODAL_SUBMIT
...additionalInteractionFields, // See the Interaction documentation for all fields
"data": {
"components": [
{
"component": {
"custom_id": "file_upload",
"id": 2,
"type": 19,
"values": [
"111111111111111111111"
]
},
"id": 1,
"type": 18
}
],
"custom_id": "bug_submit_modal",
"resolved": {
"attachments": {
"111111111111111111111": {
"content_type": "image/png",
"ephemeral": true,
"filename": "bug.png",
"height": 604,
"id": "111111111111111111111",
"placeholder": "/PcBAoBQydvKesabEIoMsdg=",
"placeholder_version": 1,
"proxy_url": "https://media.discordapp.net/ephemeral-attachments/2222222222222222222/111111111111111111111/bug.png?ex=68dc7ce1&is=68db2b61&hm=5954f90117ccf8716ffa6c7f97a778a0d039810c9584045f400d8a9fff590768&",
"size": 241394,
"url": "https://cdn.discordapp.com/ephemeral-attachments/2222222222222222222/111111111111111111111/bug.png?ex=68dc7ce1&is=68db2b61&hm=5954f90117ccf8716ffa6c7f97a778a0d039810c9584045f400d8a9fff590768&",
"width": 2482
}
}
}
}
}
```
***
## Radio Group
A Radio Group is an interactive component for selecting exactly one option from a defined list. Radio Groups are available in modals and must be placed inside a [Label](/developers/components/reference#label).
### Radio Group Structure
| Field | Type | Description |
| ---------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| type | integer | `21` for radio group |
| id? | integer | Optional identifier for component |
| custom\_id | string | Developer-defined identifier for the input; 1-100 characters |
| options | array of [radio group options](/developers/components/reference#radio-group-option-structure) | List of options to show; min 2, max 10 |
| required? | boolean | Whether a selection is required to submit the modal (defaults to `true`) |
### Radio Group Option Structure
| Field | Type | Description |
| ------------ | ------- | ------------------------------------------------------- |
| value | string | Dev-defined value of the option; max 100 characters |
| label | string | User-facing label of the option; max 100 characters |
| description? | string | Optional description for the option; max 100 characters |
| default? | boolean | Shows the option as selected by default |
### Radio Group Interaction Response Structure
| Field | Type | Description |
| ---------- | ------- | -------------------------------------------------------------------- |
| type | integer | `21` for a Radio Group |
| id | integer | Unique identifier for the component |
| custom\_id | string | Developer-defined identifier for the input; 1-100 characters |
| value | ?string | The value of the selected option, or `null` if no option is selected |
### Examples
```jsonc theme={"system"}
{
"type": 9,
"data": {
"custom_id": "class_selection_modal",
"title": "Class Selection",
"components": [
{
"type": 18, // ComponentType.LABEL
"label": "Choose your class",
"description": "Your class detertmines the style of play for your character.",
"component": {
"type": 21, // ComponentType.RADIO_GROUP
"custom_id": "class_radio",
"options": [
{"value": "warrior", "label": "Warrior", "description": "Strong and brave"},
{"value": "rogue", "label": "Rogue", "description": "Weak and squishy"},
{"value": "wizard", "label": "Wizard", "description": "Nerd"},
{"value": "bard", "label": "Bard", "description": "Annoys everyone"},
{"value": "witch_doctor", "label": "Witch Doctor", "description": "Actually a pretty cool option"}
]
}
}
]
}
}
```
```jsonc theme={"system"}
{
"type": 5, // InteractionType.MODAL_SUBMIT
...additionalInteractionFields, // See the Interaction documentation for all fields
"data": {
"custom_id": "class_selection_modal",
"components": [
{
"id": 1,
"type": 18, // ComponentType.LABEL
"component": {
"custom_id": "class_radio",
"id": 2,
"type": 21, // ComponentType.RADIO_GROUP
"value": "warrior"
}
}
]
}
}
```
***
## Checkbox Group
A Checkbox Group is an interactive component for selecting one or many options via checkboxes. Checkbox Groups are available in modals and must be placed inside a [Label](/developers/components/reference#label).
### Checkbox Group Structure
| Field | Type | Description |
| -------------- | --------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| type | integer | `22` for checkbox group |
| id? | integer | Optional identifier for component |
| custom\_id | string | Developer-defined identifier for the input; 1-100 characters |
| options | array of [checkbox group options](/developers/components/reference#checkbox-group-option-structure) | List of options to show; min 1, max 10 |
| min\_values?\* | integer | Minimum number of items that must be chosen; min 0, max 10 (defaults to 1); |
| max\_values? | integer | Maximum number of items that can be chosen; min 1, max 10 (defaults to the number of options) |
| required? | boolean | Whether selecting within the group is required (defaults to `true`) |
\* `min_values` must be either omitted or at least `1` if `required` is omitted or `true`.
### Checkbox Group Option Structure
| Field | Type | Description |
| ------------ | ------- | ------------------------------------------------------- |
| value | string | Dev-defined value of the option; max 100 characters |
| label | string | User-facing label of the option; max 100 characters |
| description? | string | Optional description for the option; max 100 characters |
| default? | boolean | Shows the option as selected by default |
### Checkbox Group Interaction Response Structure
| Field | Type | Description |
| ---------- | ---------------- | ------------------------------------------------------------------------------------- |
| type | integer | `22` for a Checkbox Group |
| id | integer | Unique identifier for the component |
| custom\_id | string | Developer-defined identifier for the input; 1-100 characters |
| values | array of strings | The values of the selected options, or an empty array `[]` if no options are selected |
### Examples
```jsonc theme={"system"}
{
"type": 9,
"data": {
"custom_id": "day_selection_modal",
"title": "Study Days",
"components": [
{
"type": 18, // ComponentType.LABEL
"label": "Which days are you free?",
"description": "Choose all of the days you're able to meet up.",
"component": {
"type": 22, // ComponentType.CHECKBOX_GROUP
"custom_id": "event_checkbox",
"options": [
{"value": "march-4", "label": "March 4th"},
{"value": "march-5", "label": "March 5th"},
{"value": "march-7", "label": "March 7th", "description": "I know this is a Saturday and is tough"},
{"value": "march-9", "label": "March 9th"},
{"value": "march-10", "label": "March 10th"}
]
}
}
]
}
}
```
```jsonc theme={"system"}
{
"type": 5, // InteractionType.MODAL_SUBMIT
...additionalInteractionFields, // See the Interaction documentation for all fields
"data": {
"custom_id": "day_selection_modal",
"components": [
{
"id": 1,
"type": 18, // ComponentType.LABEL
"component": {
"custom_id": "event_checkbox",
"id": 2,
"type": 22, // ComponentType.CHECKBOX_GROUP
"values": [
"march-5",
"march-10",
"march-4"
]
}
}
]
}
}
```
***
## Checkbox
A Checkbox is a single interactive component for simple yes/no style questions. Checkboxes are available in modals and must be placed inside a [Label](/developers/components/reference#label).
### Checkbox Structure
| Field | Type | Description |
| ---------- | ------- | ------------------------------------------------------------ |
| type | integer | `23` for checkbox |
| id? | integer | Optional identifier for component |
| custom\_id | string | Developer-defined identifier for the input; 1-100 characters |
| default? | boolean | Whether the checkbox is selected by default |
While you can't set a checkbox as required, you can use a [Checkbox Group](/developers/components/reference#checkbox-group) with a single option and `required` to achieve similar functionality.
### Checkbox Interaction Response Structure
| Field | Type | Description |
| ---------- | ------- | ------------------------------------------------------------------- |
| type | integer | `23` for a Checkbox |
| id | integer | Unique identifier for the component |
| custom\_id | string | Developer-defined identifier for the input; 1-100 characters |
| value | boolean | The state of the checkbox (`true` if checked, `false` if unchecked) |
### Examples
```jsonc theme={"system"}
{
"type": 9,
"data": {
"custom_id": "secret_note_modal",
"title": "Secret Note",
"components": [
{
"type": 18, // ComponentType.LABEL
"label": "Do you like me?",
"description": "😳😳😳",
"component": {
"type": 23, // ComponentType.CHECKBOX
"custom_id": "like_checkbox"
}
}
]
}
}
```
```jsonc theme={"system"}
{
"type": 5, // InteractionType.MODAL_SUBMIT
...additionalInteractionFields, // See the Interaction documentation for all fields
"data": {
"custom_id": "secret_note_modal",
"components": [
{
"id": 1,
"type": 18, // ComponentType.LABEL
"component": {
"custom_id": "like_checkbox",
"id": 2,
"type": 23, // ComponentType.CHECKBOX
"value": true
}
}
]
}
}
```
***
## Unfurled Media Item
An Unfurled Media Item is a piece of media, represented by a URL, that is used within a component. It can be
constructed via either uploading media to Discord, or by referencing external media via **a direct link** to the asset.
While the structure below is the full representation of an Unfurled Media Item,
**only the `url` field is settable by developers** when making requests that utilize this structure.
All other fields will be automatically populated by Discord.
###### Unfurled Media Item Structure
| Field | Type | Description |
| ------------------------ | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| url | string | Supports arbitrary urls and `attachment://` references |
| proxy\_url? \* | string | The proxied url of the media item |
| height? \* | ?integer | The height of the media item (if image or video) |
| width? \* | ?integer | The width of the media item (if image or video) |
| placeholder? \* | string | [Thumbhash](https://evanw.github.io/thumbhash/) placeholder (if image or video) |
| placeholder\_version? \* | integer | Version of the placeholder (if image or video) |
| content\_type? \* | string | The [media type](https://en.wikipedia.org/wiki/Media_type) of the content |
| flags? \* | integer | [Unfurled media item flags](/developers/components/reference#unfurled-media-item-unfurled-media-item-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) |
| attachment\_id? \* \*\* | snowflake | The id of the uploaded attachment |
\* This field is ignored and provided by the API as part of the response.
\*\* Only present if the media item was uploaded as an attachment.
###### Unfurled Media Item Flags
| Flag | Value | Description |
| ------------ | -------- | ---------------------- |
| IS\_ANIMATED | `1 << 0` | This image is animated |
### Uploading a file
To upload a file with your message, you'll need to send your payload as `multipart/form-data` (rather than `application/json`) and include your file with a valid filename in your payload. Details and examples for uploading files can be found in the [API Reference](/developers/reference#uploading-files).
## Legacy Message Component Behavior
Before the introduction of the `IS_COMPONENTS_V2` flag ([see changelog](/developers/change-log#introducing-new-components-for-messages)), message components were sent in conjunction with message content. This means that you could send a message using a subset of the available components without setting the `IS_COMPONENTS_V2` flag, and the components would be included in the message content along with `content` and `embeds`.
Additionally, components of messages preceding components V2 will contain an `id` of `0`.
Apps using this Legacy Message Component behavior will continue to work as expected, but it is recommended to use the new `IS_COMPONENTS_V2` flag for new apps or features as they offer more options for layout and customization.
Legacy messages allow up to 5 action rows as top-level components
Legacy Message Component Example
```jsonc theme={"system"}
{
"content": "This is a message with legacy components",
"components": [
{
"type": 1,
"components": [
{
"type": 2,
"style": 1,
"label": "Click Me",
"custom_id": "click_me_1"
}
]
}
]
}
```
# Using Message Components
Source: https://docs.discord.com/developers/components/using-message-components
Learn how to send interactive message components.
## Overview
Message components are a powerful way to add interactivity to your messages. They allow you to create rich, interactive experiences for your users, making it easier for them to engage with your content.
If you are sending components as part of a [webhook](/developers/resources/webhook) you'll need to use the [`?with_components=true`](/developers/resources/webhook#execute-webhook-query-string-params) query param otherwise they'll be ignored.
### Prerequisites
* You must have a Discord account and be a member of the Discord Developer Portal.
* You must have a Discord application created in the Discord Developer Portal.
* You must have the necessary permissions to send messages in the channel where you want to use components.
***
## Sending a Message with a Component
To send a message with a component, you need to set the `IS_COMPONENTS_V2` flag (`1<<15`) in your message's `flags` field. This can be done when using [Message Create](/developers/resources/message#create-message), [Execute Webhook](/developers/resources/webhook#execute-webhook), or [responding to an interaction](/developers/interactions/receiving-and-responding#create-followup-message).
Setting the `IS_COMPONENTS_V2` message flag cannot be reverted: once the message has been sent, the flag cannot be removed from the message when editing the message.
This flag indicates that the message contains components and disables traditional content and embeds.
All content must be sent as components instead of using the standard message format.
```json theme={"system"}
{
"flags": 32768,
"components": [
{
"type": 10,
"content": "This is a message using the Text Display component"
}
]
}
```
## Sending a Message with Multiple Components
To send a message with multiple components, you can include multiple component objects in your message's `components` field. This field allows you to specify an array of components that will be included in the message.
```json theme={"system"}
{
"flags": 32768,
"components": [
{
"type": 10,
"content": "This is a Text Display component."
},
{
"type": 10,
"content": "This is another Text Display component!"
}
]
}
```
## Nesting Components with Layout Components
You can also nest components within layout components. This gives you more flexibility in displaying information, images, and interactive components to your users. Check out the [list of components](/developers/components/reference#component-object-component-types) for a complete list of available layout components.
For example, you can create a message with an Action Row component that contains multiple Button components.
```json theme={"system"}
{
"flags": 32768,
"components": [
{
"type": 10,
"content": "This is a message with v2 components"
},
{
"type": 1,
"components": [
{
"type": 2,
"style": 1,
"label": "Click Me",
"custom_id": "click_me_1"
},
{
"type": 2,
"style": 2,
"label": "Click Me Too",
"custom_id": "click_me_2"
}
]
}
]
}
```
## Using Message Components with Interactions
When a user interacts with an interactive message component, your app will [receive an interaction event](/developers/interactions/overview). This event contains information about the interaction, including the type of interaction and the component that was interacted with.
See the [list of supported component types](/developers/components/reference#component-object-component-types) for a list of interactive message components and their interaction event payloads.
You can use this information to respond to the interaction, update the message, or perform other actions, such as displaying a modal based on the user's input.
Check out the [Interactions documentation](/developers/interactions/overview) for more information on handling interactions and responding to user input from interactive components.
# Using Modal Components
Source: https://docs.discord.com/developers/components/using-modal-components
Guide to creating and displaying modal dialogs with text input fields.
## Overview
Modal components are a great way to collect freeform information from your users.
### Prerequisites
* You must have a Discord account and be a member of the Discord Developer Portal.
* You must have a Discord application created in the Discord Developer Portal.
***
## Displaying a Modal
Displaying a modal can be done in response to an [interaction](/developers/interactions/receiving-and-responding). When displaying a modal you'll use an [interaction response](/developers/interactions/receiving-and-responding#interaction-response-object) with the [`MODAL`](/developers/interactions/receiving-and-responding#interaction-response-object-interaction-callback-type) interaction callback type and [modal fields](/developers/interactions/receiving-and-responding#interaction-response-object-modal).
An example of a modal with a [String Select](/developers/components/reference#string-select) and [Text Input](/developers/components/reference#text-input) both wrapped in [Labels](/developers/components/reference#label):
```json theme={"system"}
{
"type": 9,
"data": {
"custom_id": "bug_modal",
"title": "Bug Report",
"components": [
{
"type": 18,
"label": "What's your favorite bug?",
"component": {
"type": 3,
"custom_id": "bug_string_select",
"placeholder": "Choose...",
"options": [
{
"label": "Ant",
"value": "ant",
"description": "(best option)",
"emoji": {
"name": "🐜"
}
},
{
"label": "Butterfly",
"value": "butterfly",
"emoji": {
"name": "🦋"
}
},
{
"label": "Caterpillar",
"value": "caterpillar",
"emoji": {
"name": "🐛"
}
}
]
}
},
{
"type": 18,
"label": "Why is it your favorite?",
"description": "Please provide as much detail as possible!",
"component": {
"type": 4,
"custom_id": "bug_explanation",
"style": 2,
"min_length": 1000,
"max_length": 4000,
"placeholder": "Write your explanation here...",
"required": true
}
}
]
}
}
```
# Community Resources
Source: https://docs.discord.com/developers/developer-tools/community-resources
Discover community-built libraries, tools, and resources for Discord development.
Our community members create amazing tools and resources that help developers build and maintain their apps. From permissions calculators and embed visualizers to comprehensive libraries for interfacing with our API, our community has built a wealth of resources to make your development process smoother and more efficient.
## Discord Developers
The [Official Discord Developers server](https://discord.gg/discord-developers) is a developer ran, but community driven, support hub. If you need help with developing something on Discord or want official updates from the developers, this is the place to be.
## Libraries
Discord does not maintain official SDKs. The following table is an inexhaustive list of third-party libraries that have valid rate limit implementations, are recently maintained, and have large communities of active bots.
###### Discord Libraries
| Name | Language |
| ------------------------------------------------------------- | ---------- |
| [Concord](https://github.com/Cogmasters/concord) | C |
| [Discord.Net](https://github.com/discord-net/Discord.Net) | C# |
| [DSharpPlus](https://github.com/DSharpPlus/DSharpPlus) | C# |
| [D++](https://github.com/brainboxdotcc/DPP) | C++ |
| [discljord](https://github.com/discljord/discljord) | Clojure |
| [DiscordGo](https://github.com/bwmarrin/discordgo) | Go |
| [Discord4J](https://github.com/Discord4J/Discord4J) | Java |
| [JDA](https://github.com/DV8FromTheWorld/JDA) | Java |
| [discord.js](https://github.com/discordjs/discord.js) | JavaScript |
| [Eris](https://github.com/abalabahaha/eris) | JavaScript |
| [Oceanic](https://github.com/OceanicJS/Oceanic) | JavaScript |
| [Discordia](https://github.com/SinisterRectus/Discordia) | Lua |
| [DiscordPHP](https://github.com/discord-php/DiscordPHP) | PHP |
| [discord.py](https://github.com/Rapptz/discord.py) | Python |
| [disnake](https://github.com/DisnakeDev/disnake) | Python |
| [hikari](https://github.com/hikari-py/hikari) | Python |
| [interactions.py](https://github.com/interactions-py/library) | Python |
| [nextcord](https://github.com/nextcord/nextcord) | Python |
| [pycord](https://github.com/Pycord-Development/pycord) | Python |
| [discordrb](https://github.com/shardlab/discordrb) | Ruby |
| [Serenity](https://github.com/serenity-rs/serenity) | Rust |
## Interactions
[Interactions](/developers/interactions/receiving-and-responding) are the great, new way of making a Discord bot. The following open-source libraries provide help for the security and authentication checks that are mandatory if you are receiving Interactions via outgoing webhook. They also include some types for the Interactions data models.
* C#
* [Discord.Net.Rest](https://github.com/discord-net/Discord.Net)
* [DSharpPlus.Http.AspNetCore](https://github.com/DSharpPlus/DSharpPlus)
* Clojure
* [ring-discord-auth](https://github.com/JohnnyJayJay/ring-discord-auth)
* Dart
* [nyxx\_interactions](https://github.com/l7ssha/Nyxx)
* Go
* [tempest](https://github.com/amatsagu/tempest)
* Javascript
* [discord-interactions-js](https://github.com/discord/discord-interactions-js)
* [discord-slash-commands](https://github.com/MeguminSama/discord-slash-commands) and its [Deno fork](https://deno.land/x/discord_slash_commands)
* [slash-create](https://github.com/Snazzah/slash-create)
* Python
* [discord-interactions-python](https://github.com/discord/discord-interactions-python)
* [discord-interactions.py](https://github.com/LiBa001/discord-interactions.py)
* [dispike](https://github.com/ms7m/dispike)
* [flask-discord-interactions](https://github.com/breqdev/flask-discord-interactions)
* PHP
* [discord-interactions-php](https://github.com/discord/discord-interactions-php)
* Other
* [caddy-discord-interactions-verifier](https://github.com/CarsonHoffman/caddy-discord-interactions-verifier)
* [BotForge's Application Commands Builder & Previewer](https://tools.botforge.org/appbuilder)
* [Bsati's Slash Command Builder](https://bsati.github.io/dc-app-command-builder/)
## OpenAPI Specification
The OpenAPI spec is currently in public preview and **is subject to breaking changes**
The public preview of the [Discord HTTP API specification](https://github.com/discord/discord-api-spec) provides a standard [OpenAPI 3.1 spec](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md) for the HTTP API.
## Permission Calculators
[Permissions](/developers/topics/permissions) in Discord are tricky. Luckily, we've got really smart people who love us and have made some great permissions calculators. If you're making a bot for others, and you're not sure how to properly calculate permissions or generate your [authorization URL](/developers/topics/oauth2#bot-authorization-flow), these are great tools:
* [BotForge's Permissions Calculator](https://tools.botforge.org/permissions)
* [FiniteReality's Permissions Calculator](https://finitereality.github.io/permissions-calculator/?v=0)
* [abalabahaha's Permissions Calculator](https://discordapi.com/permissions.html#0)
## Intent Calculators
[Gateway Intents](/developers/events/gateway#gateway-intents) are pretty confusing at first. If you're not sure what to send in your [Identify payload](/developers/events/gateway-events#identify), then these tools may be of help:
* [ziad87's Intent Calculator](https://ziad87.net/intents/)
* [Larko's Intent Calculator](https://discord-intents-calculator.vercel.app/)
## Embed Visualizers
Webhooks and embeds might seem like black magic. That's because they are, but let us help you demystify them a bit. These tools can help you test how embeds will appear inside of Discord:
* [JohnyTheCarrot's Embed Previewer](https://github.com/JohnyTheCarrot/discord-embed-previewer) (Browser Extension)
* [AshMW's Embed Previewer](https://embedl.ink) (Embed HTML Generation)
## API Types
If you're working on a project that interacts with our API, you might find an API types module useful as it provides type inspection/completion for the Discord API.
| Name | Language |
| ------------------------------------------------------------------- | ---------- |
| [dasgo](https://github.com/switchupcb/dasgo) | Go |
| [discord-api-types](https://github.com/discordjs/discord-api-types) | JavaScript |
## Game SDK Tools
Discord Game SDK's lobby and networking layer shares similarities with other gaming platforms (i.e. Valve's Steamworks SDK). The following open source library provides developers a uniform interface for these shared features and can simplify developing for multiple platforms. Note: this library is tailored for Unity3D development.
* [HouraiNetworking](https://github.com/HouraiTeahouse/HouraiNetworking)
# Embedded App SDK Reference
Source: https://docs.discord.com/developers/developer-tools/embedded-app-sdk
The Embedded App SDK handles making RPC calls between your application and Discord. It is designed to assist developers in developing interactive Activities like games.
To learn more about building Activities, check out our [Building an Activity](/developers/activities/building-an-activity) tutorial or explore our [Sample Projects](/developers/activities/overview#sample-projects).
***
## Install the SDK
The Embedded App SDK is available via **[npm](https://www.npmjs.com/package/@discord/embedded-app-sdk)** and **[GitHub](https://github.com/discord/embedded-app-sdk)**.
In your frontend JavaScript project directory, install using your package manager of choice.
```
npm install @discord/embedded-app-sdk
```
After installing, you can import and instantiate the SDK in your project.
```javascript theme={"system"}
import { DiscordSDK } from "@discord/embedded-app-sdk";
const discordSdk = new DiscordSDK(DISCORD_CLIENT_ID);
```
***
## SDK Methods
| Name | Description |
| ----------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| [ready](/developers/developer-tools/embedded-app-sdk#ready) | Resolves when your app has successfully connected to the Discord client |
| [subscribe](/developers/developer-tools/embedded-app-sdk#subscribe) | Subscribe to an Embedded App SDK Event |
| [unsubscribe](/developers/developer-tools/embedded-app-sdk#unsubscribe) | Unsubscribe to an Embedded App SDK Event |
| [close](/developers/developer-tools/embedded-app-sdk#close) | Close an Embedded App |
### ready()
Resolves when your app has successfully connected to the Discord client.
#### Supported Platforms
| Web | iOS | Android |
| --- | --- | ------- |
| ✅ | ✅ | ✅ |
#### Required Scopes
No scopes required
#### Signature
ready(): Promise\
#### SDK Usage
```js theme={"system"}
async function setup() {
await discordSdk.ready();
// The rest of your app logic
}
```
***
### subscribe()
Used to subscribe to a specific event from the list of [SDK Events](/developers/developer-tools/embedded-app-sdk#sdk-events).
#### Supported Platforms
| Web | iOS | Android |
| --- | --- | ------- |
| ✅ | ✅ | ✅ |
#### Required Scopes
Depends on the event. Refer to the Required Scopes for the specific event you are subscribing to.
#### Signature
subscribe\(event: [Event](/developers/developer-tools/embedded-app-sdk#sdk-events), listener: (data: EventPayloadData\) => void, ...subscribeArgs: Partial\>): Promise\<[EventEmitter](https://nodejs.org/docs/latest/api/events.html)>
#### Usage
```js theme={"system"}
await discordSdk.subscribe("SDK_EVENT_NAME", eventHandler, args);
```
***
### unsubscribe()
Used to unsubscribe to [SDK Events](/developers/developer-tools/embedded-app-sdk#sdk-events) that your app has already subscribed to.
#### Supported Platforms
| Web | iOS | Android |
| --- | --- | ------- |
| ✅ | ✅ | ✅ |
#### Required Scopes
No scopes required
#### Signature
*The `EventPayloadData` will vary based on the event you are unsubscribing from. See the specific [event](/developers/developer-tools/embedded-app-sdk#sdk-events) for details.*
unsubscribe\(event: [Event](/developers/developer-tools/embedded-app-sdk#sdk-events), listener: (data: EventPayloadData\) => void, ...subscribeArgs: Partial\>): Promise\<[EventEmitter](https://nodejs.org/docs/latest/api/events.html)>
#### Usage
```js theme={"system"}
await discordSdk.unsubscribe("SDK_EVENT_NAME");
```
***
### close()
Used to close your app with a specified code and reason.
#### Supported Platforms
| Web | iOS | Android |
| --- | --- | ------- |
| ✅ | ✅ | ✅ |
#### Required Scopes
No scopes required
#### Signature
close(code: [RPCCloseCodes](/developers/developer-tools/embedded-app-sdk#rpcclosecodes), message: string): void
#### SDK Usage
```js theme={"system"}
discordSdk.close(RPCCloseCodes.CLOSE_NORMAL, "You exited from app");
```
***
## SDK Commands
Developers can use these commands to interact with the Discord client. The following SDK commands are prefixed with `.commands`, such as, `discordSDK.commands.authenticate`.
| Name | Description |
| ----------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [authenticate](/developers/developer-tools/embedded-app-sdk#authenticate) | Authenticate an existing client with your app |
| [authorize](/developers/developer-tools/embedded-app-sdk#authorize) | Authorize a new client with your app |
| [captureLog](/developers/developer-tools/embedded-app-sdk#capturelog) | Forward logs to your own logger |
| [encourageHardwareAcceleration](/developers/developer-tools/embedded-app-sdk#encouragehardwareacceleration) | Presents a modal dialog to allow enabling of hardware acceleration |
| [getChannel](/developers/developer-tools/embedded-app-sdk#getchannel) | Returns information about the channel, per the channel\_id |
| [getChannelPermissions](/developers/developer-tools/embedded-app-sdk#getchannelpermissions) | Returns permissions for the current user in the currently connected channel |
| [getEntitlements](/developers/developer-tools/embedded-app-sdk#getentitlements) | Returns a list of entitlements for the current user |
| [getInstanceConnectedParticipants](/developers/developer-tools/embedded-app-sdk#getinstanceconnectedparticipants) | Returns all participants connected to the instance |
| [getPlatformBehaviors](/developers/developer-tools/embedded-app-sdk#getplatformbehaviors) | Returns information about supported platform behaviors |
| [getRelationships](/developers/developer-tools/embedded-app-sdk#getrelationships) | Allows your app to access a user’s Discord Friends list, their pending requests, and blocked users. This scope is part of our Social SDK - [submit for access here](https://discord.com/developers/applications/select/social-sdk/getting-started). [Social SDK Terms apply](https://support-dev.discord.com/hc/en-us/articles/30225844245271-Discord-Social-SDK-Terms), including Section 5(a)(ii) to the data you obtain |
| [getSkus](/developers/developer-tools/embedded-app-sdk#getskus) | Returns a list of your app's SKUs |
| [initiateImageUpload](/developers/developer-tools/embedded-app-sdk#initiateimageupload) | Presents the file upload flow in the Discord client |
| [openExternalLink](/developers/developer-tools/embedded-app-sdk#openexternallink) | Allows for opening an external link from within the Discord client |
| [openInviteDialog](/developers/developer-tools/embedded-app-sdk#openinvitedialog) | Presents a modal dialog with Channel Invite UI without requiring additional OAuth scopes |
| [openShareMomentDialog](/developers/developer-tools/embedded-app-sdk#opensharemomentdialog) | Presents a modal dialog to share media to a channel or DM |
| [setActivity](/developers/developer-tools/embedded-app-sdk#setactivity) | Modifies how your activity's rich presence is displayed in the Discord client |
| [setConfig](/developers/developer-tools/embedded-app-sdk#setconfig) | Set whether or not the PIP (picture-in-picture) is interactive |
| [setOrientationLockState](/developers/developer-tools/embedded-app-sdk#setorientationlockstate) | Set options for orientation and picture-in-picture (PIP) modes |
| [shareLink](/developers/developer-tools/embedded-app-sdk#sharelink) | Presents a modal for the user to share a link to your activity with custom query params |
| [startPurchase](/developers/developer-tools/embedded-app-sdk#startpurchase) | Launches the purchase flow for a specific SKU, per the sku\_id |
| [userSettingsGetLocale](/developers/developer-tools/embedded-app-sdk#usersettingsgetlocale) | Returns the current user's locale |
### authenticate()
Authenticate an existing client with your app.
#### Supported Platforms
| Web | iOS | Android |
| --- | --- | ------- |
| ✅ | ✅ | ✅ |
#### Required Scopes
No scopes required
#### Signature
authenticate(args: [AuthenticateRequest](/developers/developer-tools/embedded-app-sdk#authenticaterequest)): Promise\<[AuthenticateResponse](/developers/developer-tools/embedded-app-sdk#authenticateresponse)>
#### Usage
```js theme={"system"}
await discordSdk.commands.authenticate({
access_token: 'ACCESS_TOKEN_STRING'
});
```
***
### authorize()
Authorize a new client with your app.
#### Supported Platforms
| Web | iOS | Android |
| --- | --- | ------- |
| ✅ | ✅ | ✅ |
#### Required Scopes
No scopes required
#### Signature
authorize(args: [AuthorizeRequest](/developers/developer-tools/embedded-app-sdk#authorizerequest)): Promise\<[AuthorizeResponse](/developers/developer-tools/embedded-app-sdk#authorizeresponse)>
#### Usage
```js theme={"system"}
await discordSdk.commands.authorize({
client_id: DISCORD_CLIENT_ID,
response_type: "code",
state: "",
prompt: "none",
scope: [
// "applications.builds.upload",
// "applications.builds.read",
// "applications.store.update",
// "applications.entitlements",
// "bot",
"identify",
// "connections",
// "email",
// "gdm.join",
"guilds",
// "guilds.join",
// "guilds.members.read",
// "messages.read",
// "relationships.read",
// 'rpc.activities.write',
// "rpc.notifications.read",
// "rpc.voice.write",
// "rpc.voice.read",
// "webhook.incoming",
],
});
```
***
### captureLog()
Forward logs to your own logger.
#### Supported Platforms
| Web | iOS | Android |
| --- | --- | ------- |
| ✅ | ✅ | ✅ |
#### Required Scopes
No scopes required
#### Signature
captureLog(args: [CaptureLogRequest](/developers/developer-tools/embedded-app-sdk#capturelogrequest)): Promise\
#### Usage
```js theme={"system"}
await discordSdk.commands.captureLog({
level: 'log',
message: 'This is my log message!'
});
```
***
### encourageHardwareAcceleration()
Presents a modal dialog to allow enabling of hardware acceleration.
#### Supported Platforms
| Web | iOS | Android |
| --- | --- | ------- |
| ✅ | ⛔️ | ⛔️ |
#### Required Scopes
No scopes required
#### Signature
encourageHardwareAcceleration(): Promise\<[EncourageHardwareAccelerationResponse](/developers/developer-tools/embedded-app-sdk#encouragehardwareaccelerationresponse)>
#### Usage
```js theme={"system"}
await discordSdk.commands.encourageHardwareAcceleration();
```
***
### getChannel()
Returns information about the channel for a provided channel ID.
#### Supported Platforms
| Web | iOS | Android |
| --- | --- | ------- |
| ✅ | ✅ | ✅ |
#### Required Scopes
* \[guilds] for guild channels
* \[guilds, dm\_channels.read] for GDM channels. dm\_channels.read requires approval from Discord.
#### Signature
getChannel(args: [GetChannelRequest](/developers/developer-tools/embedded-app-sdk#getchannelrequest)): Promise\<[GetChannelResponse](/developers/developer-tools/embedded-app-sdk#getchannelresponse)>
#### Usage
```js theme={"system"}
await discordSdk.commands.getChannel({
channel_id: discordSdk.channelId,
});
```
***
### getChannelPermissions()
Returns permissions for the current user in the currently connected channel.
#### Supported Platforms
| Web | iOS | Android |
| --- | --- | ------- |
| ✅ | ✅ | ✅ |
#### Required Scopes
* guilds.members.read
#### Signature
getChannelPermissions(): Promise\<[GetChannelPermissionsResponse](/developers/developer-tools/embedded-app-sdk#getchannelpermissionsresponse)>
#### Usage
```js theme={"system"}
await discordSdk.commands.getChannelPermissions();
```
***
### getEntitlements()
Returns a list of entitlements for the current user.
#### Supported Platforms
| Web | iOS | Android |
| --- | --- | ------- |
| ✅ | ✅ | ✅ |
#### Required Scopes
No scopes required
#### Signature
getEntitlements(): Promise\<[GetEntitlementsResponse](/developers/developer-tools/embedded-app-sdk#getentitlementsresponse)>
#### Usage
```js theme={"system"}
await discordSdk.commands.getEntitlements();
```
***
### getInstanceConnectedParticipants()
Returns all participants connected to the instance.
#### Supported Platforms
| Web | iOS | Android |
| --- | --- | ------- |
| ✅ | ✅ | ✅ |
#### Required Scopes
No scopes required
#### Signature
getInstanceConnectedParticipants(): Promise\<[GetInstanceConnectedParticipantsResponse](/developers/developer-tools/embedded-app-sdk#getinstanceconnectedparticipantsresponse)>
#### Usage
```js theme={"system"}
await discordSdk.commands.getInstanceConnectedParticipants();
```
***
### getPlatformBehaviors()
Returns information about supported platform behaviors.
#### Supported Platforms
| Web | iOS | Android |
| --- | --- | ------- |
| ✅ | ✅ | ✅ |
#### Required Scopes
No scopes required
#### Signature
getPlatformBehaviors(): Promise\<[GetPlatformBehaviorsResponse](/developers/developer-tools/embedded-app-sdk#getplatformbehaviorsresponse)>
#### Usage
```js theme={"system"}
await discordSdk.commands.getPlatformBehaviors();
```
***
### getRelationships()
Returns the current user's relationships.
#### Supported Platforms
| Web | iOS | Android |
| --- | --- | ------- |
| ✅ | ✅ | ✅ |
#### Required Scopes
* relationships.read
This scope is part of our Social SDK - [submit for access here](https://discord.com/developers/applications/select/social-sdk/getting-started). [Social SDK Terms apply](https://support-dev.discord.com/hc/en-us/articles/30225844245271-Discord-Social-SDK-Terms), including Section 5(a)(ii) to the data you obtain
#### Signature
getRelationships(): Promise\<[GetRelationshipsResponse](/developers/developer-tools/embedded-app-sdk#getrelationshipsresponse)>
#### Usage
```js theme={"system"}
await discordSdk.commands.getRelationships();
```
***
### getSkus()
Returns a list of SKU objects. SKUs without prices are automatically filtered out.
#### Supported Platforms
| Web | iOS | Android |
| --- | --- | ------- |
| ✅ | ✅ | ✅ |
#### Required Scopes
No scopes required
#### Signature
getSkus(): Promise\<[GetSkusResponse](/developers/developer-tools/embedded-app-sdk#getskusresponse)>
#### Usage
```js theme={"system"}
await discordSdk.commands.getSkus();
```
***
### initiateImageUpload()
Presents the file upload flow in the Discord client.
#### Supported Platforms
| Web | iOS | Android |
| --- | --- | ------- |
| ✅ | ✅ | ✅ |
#### Required Scopes
No scopes required
#### Signature
initiateImageUpload(): Promise\<[InitiateImageUploadResponse](/developers/developer-tools/embedded-app-sdk#initiateimageuploadresponse)>
#### Usage
```js theme={"system"}
await discordSdk.commands.initiateImageUpload();
```
***
### openExternalLink()
Allows for opening an external link from within the Discord client.
#### Supported Platforms
| Web | iOS | Android |
| --- | --- | ------- |
| ✅ | ✅ | ✅ |
#### Required Scopes
No scopes required
#### Signature
openExternalLink(args: [OpenExternalLinkRequest](/developers/developer-tools/embedded-app-sdk#openexternallinkrequest)): Promise\<[OpenExternalLinkResponse](/developers/developer-tools/embedded-app-sdk#openexternallinkresponse)>
#### Usage
```js theme={"system"}
await discordSdk.commands.openExternalLink({
url: 'string url'
});
```
***
### openInviteDialog()
Presents a modal dialog with Channel Invite UI without requiring additional OAuth scopes.
#### Supported Platforms
| Web | iOS | Android |
| --- | --- | ------- |
| ✅ | ✅ | ✅ |
#### Required Scopes
No scopes required
#### Signature
openInviteDialog(): Promise\
#### Usage
```js theme={"system"}
await discordSdk.commands.openInviteDialog();
```
***
### openShareMomentDialog()
Presents a modal dialog to share media to a channel or direct message.
#### Supported Platforms
| Web | iOS | Android |
| --- | --- | ------- |
| ✅ | ⛔️ | ⛔️ |
#### Required Scopes
No scopes required
#### Signature
openShareMomentDialog(args: [OpenShareMomentDialogRequest](/developers/developer-tools/embedded-app-sdk#opensharemomentdialogrequest)) Promise\
#### Usage
```js theme={"system"}
await discordSdk.commands.openShareMomentDialog({
mediaUrl: 'DISCORD_CDN_URL'
});
```
***
### setActivity()
Modifies how your Activity's Rich Presence data is displayed in the Discord client. The inner `activity` field is a partial [Activity object](/developers/events/gateway-events#activity-object-activity-structure).
Read the guide on [Using Rich Presence with the Embedded App SDK](/developers/rich-presence/using-with-the-embedded-app-sdk) for more usage details.
#### Supported Platforms
| Web | iOS | Android |
| --- | --- | ------- |
| ✅ | ✅ | ✅ |
#### Required Scopes
* rpc.activities.write
#### Signature
setActivity(args: [SetActivityRequest](/developers/developer-tools/embedded-app-sdk#setactivityrequest)): Promise\<[Activity](/developers/developer-tools/embedded-app-sdk#activity)>
#### Usage
```js theme={"system"}
await discordSdk.commands.setActivity({
activity: {
type: 0,
details: 'Details',
state: 'Playing'
}
});
```
***
### setConfig()
Set whether or not the PIP (picture-in-picture) is interactive.
#### Supported Platforms
| Web | iOS | Android |
| --- | --- | ------- |
| ✅ | ⛔️ | ⛔️ |
#### Required Scopes
No scopes required
#### Signature
setConfig(args: [SetConfigRequest](/developers/developer-tools/embedded-app-sdk#setconfigrequest)): Promise\<[SetConfigResponse](/developers/developer-tools/embedded-app-sdk#setconfigresponse)>
#### Usage
```js theme={"system"}
await discordSdk.commands.setConfig({
use_interactive_pip: true
})
```
***
### setOrientationLockState()
Locks the application to specific orientations in each of the supported layout modes.
#### Supported Platforms
| Web | iOS | Android |
| --- | --- | ------- |
| ⛔️ | ✅ | ✅ |
#### Required Scopes
No scopes required
#### Signature
setOrientationLockState(args: [SetOrientationLockStateRequest](/developers/developer-tools/embedded-app-sdk#setorientationlockstaterequest)): Promise\
#### Usage
```js theme={"system"}
import {Common} from '@discord/embedded-app-sdk';
await discordSdk.commands.setOrientationLockState({
lock_state: Common.OrientationLockStateTypeObject.LANDSCAPE,
picture_in_picture_lock_state: Common.OrientationLockStateTypeObject.LANDSCAPE,
grid_lock_state: Common.OrientationLockStateTypeObject.UNLOCKED
});
```
***
### shareLink()
Presents the user with a modal to share a link
#### Supported Platforms
| Web | iOS | Android |
| --- | --- | ------- |
| ✅ | ✅ | ✅ |
#### Required Scopes
No scopes required
#### Signature
shareLink(args: [ShareLinkRequest](/developers/developer-tools/embedded-app-sdk#sharelinkrequest)): Promise\<[ShareLinkResponse](/developers/developer-tools/embedded-app-sdk#sharelinkresponse)>\
#### Usage
```js theme={"system"}
const { success } = await discordSdk.commands.shareLink({
message: 'This message is shared alongside the link!',
custom_id: 'some_custom_id',
});
success ? console.log('User shared link!') : console.log('User did not share link!');
```
***
### startPurchase()
Launches the purchase flow for a specific SKU ID.
#### Supported Platforms
| Web | iOS | Android |
| --- | --- | ------- |
| ✅ | ⛔️ | ⛔️ |
#### Required Scopes
No scopes required
#### Signature
startPurchase(args: [StartPurchaseRequest](/developers/developer-tools/embedded-app-sdk#startpurchaserequest)): Promise\<[StartPurchaseResponse](/developers/developer-tools/embedded-app-sdk#startpurchaseresponse)>
#### Usage
```js theme={"system"}
await discordSdk.commands.startPurchase({sku_id: skuId});
```
***
### userSettingsGetLocale()
Returns the current user's locale.
#### Supported Platforms
| Web | iOS | Android |
| --- | --- | ------- |
| ✅ | ✅ | ✅ |
#### Required Scopes
* identify
#### Signature
userSettingsGetLocale(): Promise\<[UserSettingsGetLocaleResponse](/developers/developer-tools/embedded-app-sdk#usersettingsgetlocaleresponse)>
#### Usage
```js theme={"system"}
await discordSdk.commands.userSettingsGetLocale();
```
***
## SDK Events
Developers may use the following events alongside the `subscribe()` SDK method to subscribe to events from Discord and supported devices.
| Name | Description |
| --------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| [READY](/developers/developer-tools/embedded-app-sdk#ready) | non-subscription event sent immediately after connecting, contains server information |
| [ERROR](/developers/developer-tools/embedded-app-sdk#error) | non-subscription event sent when there is an error, including command responses |
| [VOICE\_STATE\_UPDATE](/developers/developer-tools/embedded-app-sdk#voicestateupdate) | sent when a user's voice state changes in a subscribed voice channel (mute, volume, etc.) |
| [SPEAKING\_START](/developers/developer-tools/embedded-app-sdk#speakingstart) | sent when a user in a subscribed voice channel speaks |
| [SPEAKING\_STOP](/developers/developer-tools/embedded-app-sdk#speakingstop) | sent when a user in a subscribed voice channel stops speaking |
| [ACTIVITY\_LAYOUT\_MODE\_UPDATE](/developers/developer-tools/embedded-app-sdk#activitylayoutmodeupdate) | Received when a user changes the layout mode in the Discord client |
| [ORIENTATION\_UPDATE](/developers/developer-tools/embedded-app-sdk#orientationupdate) | Received when screen orientation changes |
| [CURRENT\_USER\_UPDATE](/developers/developer-tools/embedded-app-sdk#currentuserupdate) | Received when the current user object changes |
| [CURRENT\_GUILD\_MEMBER\_UPDATE](/developers/developer-tools/embedded-app-sdk#currentguildmemberupdate) | Received when the current guild member object changes |
| [THERMAL\_STATE\_UPDATE](/developers/developer-tools/embedded-app-sdk#thermalstateupdate) | Received when Android or iOS thermal states are surfaced to the Discord app |
| [ACTIVITY\_INSTANCE\_PARTICIPANTS\_UPDATE](/developers/developer-tools/embedded-app-sdk#activityinstanceparticipantsupdate) | Received when the number of instance participants changes |
| [RELATIONSHIP\_UPDATE](/developers/developer-tools/embedded-app-sdk#relationshipupdate) | Received when a relationship of the current user is updated |
| [ENTITLEMENT\_CREATE](/developers/developer-tools/embedded-app-sdk#entitlementcreate) | Received when an entitlement is created for a SKU |
### READY
Non-subscription event sent immediately after connecting, contains server information.
#### Required Scopes
No scopes required
#### Sample Event Payload
```javascript theme={"system"}
{
"v": 1,
"config": {
"cdn_host": "cdn.discordapp.com",
"api_endpoint": "//discord.com/api",
"environment": "production"
}
}
```
### ERROR
Non-subscription event sent when there is an error, including command responses.
#### Required Scopes
No scopes required
#### Sample Event Payload
```javascript theme={"system"}
{
"code": 4006,
"message": "Not authenticated or invalid scope"
}
```
***
### VOICE\_STATE\_UPDATE
Received when a user's voice state changes in a subscribed voice channel (mute, volume, etc).
#### Required Scopes
* rpc.voice.read
#### Sample Event Payload
```javascript theme={"system"}
{
"voice_state": {
"mute": false,
"deaf": false,
"self_mute": false,
"self_deaf": false,
"suppress": false
},
"user": {
"id": "190320984123768832",
"username": "test 2",
"discriminator": "7479",
"avatar": "b004ec1740a63ca06ae2e14c5cee11f3",
"bot": false
},
"nick": "test user 2",
"volume": 110,
"mute": false,
"pan": {
"left": 1.0,
"right": 1.0
}
}
```
***
### SPEAKING\_START
Received when a user in a subscribed voice channel speaks.
#### Required Scopes
* rpc.voice.read
#### Sample Event Payload
```javascript theme={"system"}
{
"channel_id": "7173758092142710784",
"user_id": "7173758143913005056"
}
```
***
### SPEAKING\_STOP
Received when a user in a subscribed voice channel stops speaking.
#### Required Scopes
* rpc.voice.read
#### Sample Event Payload
```javascript theme={"system"}
{
"channel_id": "7173758211307081728",
"user_id": "7173758261412237312"
}
```
***
### ACTIVITY\_LAYOUT\_MODE\_UPDATE
Received when a user changes the layout mode in the Discord client.
#### Required Scopes
No scopes required
#### Sample Event Payload
```javascript theme={"system"}
{
"layout_mode": 1
}
```
***
### ORIENTATION\_UPDATE
Received when screen orientation changes.
#### Required Scopes
No scopes required
#### Sample Event Payload
```javascript theme={"system"}
{
"screen_orientation": 1
}
```
***
### CURRENT\_USER\_UPDATE
Received when the current user object changes.
#### Required Scopes
* identify
#### Sample Event Payload
```javascript theme={"system"}
{
"id": "7173771622812225536",
"username": "beef_supreme",
"discriminator": "0",
"global_name": "Dis Cord",
"avatar": "abcdefg",
"avatar_decoration_data": {
"asset": "abcdefg",
"sku_id": "123456789"
},
"bot": false,
"flags": 1,
"premium_type": 2
}
```
***
### CURRENT\_GUILD\_MEMBER\_UPDATE
Received when the current guild member object changes.
#### Required Scopes
* identify
* guilds.members.read
#### Sample Event Payload
```javascript theme={"system"}
{
"user_id": "7173771622812225536",
"nick": "beef_supreme",
"guild_id": "613425648685547541"
"avatar": "abcdefg",
"avatar_decoration_data": {
"asset": "abcdefg",
"sku_id": "123456789"
},
"color_string": "#ffff00"
}
```
***
### THERMAL\_STATE\_UPDATE
Received when Android or iOS thermal states are surfaced to the Discord mobile app.
#### Required Scopes
No scopes required
#### Sample Event Payload
```javascript theme={"system"}
{
thermal_state: 0
}
```
***
### ACTIVITY\_INSTANCE\_PARTICIPANTS\_UPDATE
Received when the number of instance participants changes.
#### Required Scopes
No scopes required
#### Sample Event Payload
```javascript theme={"system"}
{
"participants": [
{
"id": "7173771622812225536",
"username": "beef_supreme",
"discriminator": "0",
"global_name": "Dis Cord",
"avatar": "abcdefg",
"avatar_decoration_data": {
"asset": "abcdefg",
"sku_id": "123456789"
},
"bot": false,
"flags": 1,
"premium_type": 2
}
]
}
```
***
### RELATIONSHIP\_UPDATE
Received when a relationship of the current user is updated.
#### Required Scopes
* relationships.read
This scope is part of our Social SDK - [submit for access here](https://discord.com/developers/applications/select/social-sdk/getting-started). [Social SDK Terms apply](https://support-dev.discord.com/hc/en-us/articles/30225844245271-Discord-Social-SDK-Terms), including Section 5(a)(ii) to the data you obtain
#### Sample Event Payload
```javascript theme={"system"}
{
"type": 1,
"user": {
"id": "7173771622812225536",
"username": "beef_supreme",
"discriminator": "0",
"global_name": "Dis Cord",
"avatar": "abcdefg",
"avatar_decoration_data": {
"asset": "abcdefg",
"sku_id": "123456789"
},
"bot": false,
"flags": 1,
"premium_type": 2
}
}
```
***
### ENTITLEMENT\_CREATE
Coming soon! Not available during Developer Preview
## SDK Interfaces
#### Activity
| Property | Type |
| ---------------- | --------------------------------------------------------------------------- |
| name | string |
| type | number |
| url? | string \| null |
| created\_at? | number \| null |
| timestamps? | [Timestamp](/developers/developer-tools/embedded-app-sdk#timestamp) \| null |
| application\_id? | string \| null |
| details? | string \| null |
| details\_url? | string \| null |
| state? | string \| null |
| state\_url? | string \| null |
| emoji? | [Emoji](/developers/developer-tools/embedded-app-sdk#emoji) \| null |
| party? | [Party](/developers/developer-tools/embedded-app-sdk#party) \| null |
| assets? | [Assets](/developers/developer-tools/embedded-app-sdk#assets) \| null |
| secrets? | [Secrets](/developers/developer-tools/embedded-app-sdk#secrets) \| null |
| instance? | boolean \| null |
| flags? | number \| null |
#### Assets
| Property | Type |
| ------------- | -------------- |
| large\_image? | string \| null |
| large\_text? | string \| null |
| large\_url? | string \| null |
| small\_image? | string \| null |
| small\_text? | string \| null |
| small\_url? | string \| null |
#### Application
| Property | Type |
| ------------- | -------------- |
| description | string |
| icon? | string \| null |
| id | string |
| rpc\_origins? | string\[] |
| name | string |
#### Attachment
| Property | Type |
| ---------- | -------------- |
| id | string |
| filename | string |
| size | number |
| url | string |
| proxy\_url | string |
| height? | number \| null |
| width? | number \| null |
#### AuthenticateRequest
| Property | Type |
| -------------- | -------------- |
| access\_token? | string \| null |
#### AuthenticateResponse
| Property | Type |
| ------------- | ----------------------------------------------------------------------- |
| access\_token | string |
| user | [User](/developers/developer-tools/embedded-app-sdk#user) |
| scopes | string\[] |
| expires | string |
| application | [Application](/developers/developer-tools/embedded-app-sdk#application) |
#### AuthorizeRequest
| Property | Type |
| ------------------------ | -------------------------------------------------------------------------- |
| client\_id | string |
| scope | [OAuthScopes](/developers/developer-tools/embedded-app-sdk#oauthscopes)\[] |
| response\_type? | 'code' |
| code\_challenge? | string |
| state? | string |
| prompt? | 'none' |
| code\_challenge\_method? | 'S256' |
#### AuthorizeResponse
| Property | Type |
| -------- | ------ |
| code | string |
#### AvatarDecorationData
| Property | Type |
| -------- | -------------- |
| asset | string |
| sku\_id? | string \| null |
#### CaptureLogRequest
| Property | Type |
| -------- | ------------------------------------------------------------------------- |
| level | [ConsoleLevel](/developers/developer-tools/embedded-app-sdk#consolelevel) |
| message | string |
#### ChannelMention
| Property | Type |
| --------- | ------ |
| id | string |
| guild\_id | string |
| type | number |
| name | string |
#### Embed
| Property | Type |
| ------------ | ----------------------------------------------------------------------------------- |
| title? | string \| null |
| type? | string \| null |
| description? | string \| null |
| url? | string \| null |
| timestamp? | string \| null |
| color? | number \| null |
| footer? | [EmbedFooter](/developers/developer-tools/embedded-app-sdk#embedfooter) \| null |
| image? | Image \| null |
| thumbnail? | Image \| null |
| video? | Video \| null |
| provider? | [EmbedProvider](/developers/developer-tools/embedded-app-sdk#embedprovider) \| null |
| author? | [EmbedAuthor](/developers/developer-tools/embedded-app-sdk#embedauthor) \| null |
| fields? | [EmbedField](/developers/developer-tools/embedded-app-sdk#embedfield)\[] \| null |
#### EmbedAuthor
| Property | Type |
| ----------------- | -------------- |
| name? | string \| null |
| url? | string \| null |
| icon\_url? | string \| null |
| proxy\_icon\_url? | string \| null |
#### EmbedField
| Property | Type |
| -------- | ------- |
| name | string |
| value | string |
| inline | boolean |
#### EmbedFooter
| Property | Type |
| ----------------- | -------------- |
| text | string |
| icon\_url? | string \| null |
| proxy\_icon\_url? | string \| null |
#### EmbedProvider
| Property | Type |
| -------- | -------------- |
| name? | string \| null |
| url? | string \| null |
#### Emoji
| Property | Type |
| ---------------- | ----------------------------------------------------------------- |
| id | string |
| name? | string \| null |
| roles? | string\[] \| null |
| user? | [User](/developers/developer-tools/embedded-app-sdk#user) \| null |
| require\_colons? | boolean \| null |
| managed? | boolean \| null |
| animated? | boolean \| null |
| available? | boolean \| null |
#### EncourageHardwareAccelerationResponse
| Property | Type |
| -------- | ------- |
| enabled | boolean |
#### Entitlement
| Property | Type |
| ---------------------- | ----------------- |
| id | string |
| sku\_id | string |
| application\_id | string |
| user\_id | string |
| gift\_code\_flags | number |
| type | string \| number |
| gifter\_user\_id? | string \| null |
| branches? | string\[] \| null |
| starts\_at? | string \| null |
| ends\_at? | string \| null |
| parent\_id? | string \| null |
| consumed? | boolean \| null |
| deleted? | boolean \| null |
| gift\_code\_batch\_id? | string \| null |
#### GetChannelPermissionsResponse
| Property | Type |
| ----------- | ---------------- |
| permissions | bigint \| string |
#### GetChannelRequest
| Property | Type |
| ----------- | ------ |
| channel\_id | string |
#### GetChannelResponse
| Property | Type |
| ------------- | ------------------------------------------------------------------------------------- |
| id | string |
| type | [ChannelTypesObject](/developers/developer-tools/embedded-app-sdk#channeltypesobject) |
| guild\_id? | string \| null |
| name? | string \| null |
| topic? | string \| null |
| bitrate? | number \| null |
| user\_limit? | number \| null |
| position? | number \| null |
| voice\_states | [UserVoiceState](/developers/developer-tools/embedded-app-sdk#uservoicestate)\[] |
| messages | [Message](/developers/developer-tools/embedded-app-sdk#message)\[] |
#### GetEntitlementsResponse
| Property | Type |
| ------------ | -------------------------------------------------------------------------- |
| entitlements | [Entitlement](/developers/developer-tools/embedded-app-sdk#entitlement)\[] |
#### GetInstanceConnectedParticipantsResponse
| Property | Type |
| ------------ | ------------------------------------------------------------ |
| participants | [User](/developers/developer-tools/embedded-app-sdk#user)\[] |
#### GetPlatformBehaviorsResponse
| Property | Type |
| ----------------------- | ------- |
| iosKeyboardResizesView? | boolean |
#### GetRelationshipsResponse
| Property | Type |
| ------------- | ---------------------------------------------------------------------------- |
| relationships | [Relationship](/developers/developer-tools/embedded-app-sdk#relationship)\[] |
#### GetSkusResponse
| Property | Type |
| -------- | ---------------------------------------------------------- |
| skus | [Sku](/developers/developer-tools/embedded-app-sdk#sku)\[] |
#### GuildMember
| Property | Type |
| ---------- | --------------------------------------------------------- |
| user | [User](/developers/developer-tools/embedded-app-sdk#user) |
| nick? | string \| null |
| roles | string\[] |
| joined\_at | string |
| deaf | boolean |
| mute | boolean |
#### GuildMemberRPC
| Property | Type |
| ------------------------- | ------------------------------------------------------------------------------------------------- |
| user\_id | string |
| nick? | string \| null |
| guild\_id | string |
| avatar? | string \| null |
| avatar\_decoration\_data? | [AvatarDecorationData](/developers/developer-tools/embedded-app-sdk#avatardecorationdata) \| null |
| color\_string? | string \| null |
#### Image
| Property | Type |
| ----------- | -------------- |
| url? | string \| null |
| proxy\_url? | string \| null |
| height? | number \| null |
| width? | number \| null |
#### InitiateImageUploadResponse
| Property | Type |
| ---------- | ------ |
| image\_url | string |
#### Message
| Property | Type | |
| -------------------- | --------------------------------------------------------------------------------------------- | -------------- |
| id | string | |
| channel\_id | string | |
| guild\_id? | string \| null | |
| author? | [User](/developers/developer-tools/embedded-app-sdk#user) \| null | |
| member? | [GuildMember](/developers/developer-tools/embedded-app-sdk#guildmember) \| null | |
| content | string | |
| timestamp | string | |
| edited\_timestamp? | string \| null | |
| tts | boolean | |
| mention\_everyone | boolean | |
| mentions | [User](/developers/developer-tools/embedded-app-sdk#user)\[] | |
| mention\_roles | string\[] | |
| mention\_channels | [ChannelMention](/developers/developer-tools/embedded-app-sdk#channelmention)\[] | |
| attachments | [Attachment](/developers/developer-tools/embedded-app-sdk#attachment)\[] | |
| embeds | [Embed](/developers/developer-tools/embedded-app-sdk#embed)\[] | |
| reactions? | [Reaction](/developers/developer-tools/embedded-app-sdk#reaction)\[] \| null | |
| nonce? | string | number \| null |
| pinned | boolean | |
| webhook\_id? | string \| null | |
| type | number | |
| activity? | [MessageActivity](/developers/developer-tools/embedded-app-sdk#messageactivity) \| null | |
| application? | [MessageApplication](/developers/developer-tools/embedded-app-sdk#messageapplication) \| null | |
| message\_reference? | [MessageReference](/developers/developer-tools/embedded-app-sdk#messagereference) \| null | |
| flags? | number | |
| stickers? | Sticker\[] \| null | |
| referenced\_message? | [Message](/developers/developer-tools/embedded-app-sdk#message) \| null | |
#### MessageActivity
| Property | Type |
| ---------- | -------------- |
| type | number |
| party\_id? | string \| null |
#### MessageApplication
| Property | Type |
| ------------- | -------------- |
| id | string |
| cover\_image? | string \| null |
| description | string |
| icon? | string \| null |
| name | string |
#### MessageReference
| Property | Type |
| ------------ | -------------- |
| message\_id? | string \| null |
| channel\_id? | string \| null |
| guild\_id? | string \| null |
#### OpenExternalLinkRequest
| Property | Type |
| -------- | ------ |
| url | string |
#### OpenExternalLinkResponse
`{ opened: null }` is returned on Discord clients before December 2024 that do not report the open link result.
| Property | Type |
| -------- | --------------- |
| opened | boolean \| null |
#### OpenShareMomentDialogRequest
| Property | Type |
| -------- | ------ |
| mediaUrl | string |
#### Party
| Property | Type |
| -------- | ----------------- |
| id? | string \| null |
| size? | number\[] \| null |
#### Reaction
| Property | Type |
| -------- | ----------------------------------------------------------- |
| count | number |
| me | boolean |
| emoji | [Emoji](/developers/developer-tools/embedded-app-sdk#emoji) |
#### Relationship
| Property | Type |
| -------- | ------------------------------------------------------------------------- |
| type | [number](/developers/developer-tools/embedded-app-sdk#relationship-types) |
| user | [User](/developers/developer-tools/embedded-app-sdk#user) |
#### Secrets
| Property | Type |
| -------- | ------ |
| join? | string |
| match? | string |
#### SetActivityRequest
| Property | Type |
| -------- | ----------------------------------------------------------------- |
| activity | [Activity](/developers/developer-tools/embedded-app-sdk#activity) |
#### SetConfigRequest
| Property | Type |
| --------------------- | ------- |
| use\_interactive\_pip | boolean |
#### SetConfigResponse
| Property | Type |
| --------------------- | ------- |
| use\_interactive\_pip | boolean |
#### SetOrientationLockStateRequest
| Property | Type |
| --------------------------------- | --------------------------------------------------------------------------------------------------- |
| lock\_state | [OrientationLockState](/developers/developer-tools/embedded-app-sdk#orientationlockstatetypeobject) |
| picture\_in\_picture\_lock\_state | [OrientationLockState](/developers/developer-tools/embedded-app-sdk#orientationlockstatetypeobject) |
| grid\_lock\_state | [OrientationLockState](/developers/developer-tools/embedded-app-sdk#orientationlockstatetypeobject) |
#### ShareLinkRequest
| Property | Type |
| ----------- | ------ |
| custom\_id? | string |
| message | string |
#### ShareLinkResponse
| Property | Type |
| -------- | ------- |
| success | boolean |
#### Sku
| Property | Type |
| --------------- | --------------------------------------------------------------------------- |
| id | string |
| name | string |
| type | [SkuTypeObject](/developers/developer-tools/embedded-app-sdk#skutypeobject) |
| price | [SkuPrice](/developers/developer-tools/embedded-app-sdk#skuprice) |
| application\_id | string |
| flags | number |
| release\_date | string \| null |
#### SkuPrice
| Property | Type |
| -------- | ------ |
| amount | number |
| currency | string |
#### StartPurchaseRequest
| Property | Type |
| -------- | ------ |
| sku\_id | string |
#### StartPurchaseResponse
| Value |
| ---------------------------------------------------------------------------------- |
| [Entitlement](/developers/developer-tools/embedded-app-sdk#entitlement)\[] \| null |
#### Timestamp
| Property | Type |
| -------- | ------ |
| start? | number |
| end? | number |
#### User
| Property | Type |
| ------------------------ | ------------------------------------------------------------------------------------------------- |
| id | string |
| username | string |
| discriminator | string |
| global\_name? | string \| null |
| avatar? | string \| null |
| avatar\_decoration\_data | [AvatarDecorationData](/developers/developer-tools/embedded-app-sdk#avatardecorationdata) \| null |
| bot | boolean |
| flags? | number \| null |
| premium\_type? | number \| null |
#### UserSettingsGetLocaleResponse
| Property | Type |
| -------- | ------ |
| locale | string |
#### UserVoiceState
| Property | Type |
| ------------ | --------------------------------------------------------------------- |
| mute | boolean |
| nick | string |
| user | [User](/developers/developer-tools/embedded-app-sdk#user) |
| voice\_state | [VoiceState](/developers/developer-tools/embedded-app-sdk#voicestate) |
| volume | number |
#### Video
| Property | Type |
| -------- | -------------- |
| url? | string \| null |
| height? | number \| null |
| width? | number \| null |
#### VoiceState
| Property | Type |
| ---------- | ------- |
| mute | boolean |
| deaf | boolean |
| self\_mute | boolean |
| self\_deaf | boolean |
| suppress | boolean |
## SDK Enums
#### ChannelTypesObject
| Name | Value |
| -------------------- | ----- |
| UNHANDLED | -1 |
| DM | 1 |
| GROUP\_DM | 3 |
| GUILD\_TEXT | 0 |
| GUILD\_VOICE | 2 |
| GUILD\_CATEGORY | 4 |
| GUILD\_ANNOUNCEMENT | 5 |
| GUILD\_STORE | 6 |
| ANNOUNCEMENT\_THREAD | 10 |
| PUBLIC\_THREAD | 11 |
| PRIVATE\_THREAD | 12 |
| GUILD\_STAGE\_VOICE | 13 |
| GUILD\_DIRECTORY | 14 |
| GUILD\_FORUM | 15 |
#### ConsoleLevel
| Value |
| ------- |
| 'error' |
| 'log' |
| 'warn' |
| 'debug' |
| 'info' |
#### OrientationLockStateTypeObject
| Name | Value |
| --------- | ----- |
| UNHANDLED | -1 |
| UNLOCKED | 1 |
| PORTRAIT | 2 |
| LANDSCAPE | 3 |
#### ThermalStateTypeObject
| Name | Value |
| --------- | ----- |
| UNHANDLED | -1 |
| NOMINAL | 0 |
| FAIR | 1 |
| SERIOUS | 2 |
| CRITICAL | 3 |
#### OrientationTypeObject
| Name | Value |
| --------- | ----- |
| UNHANDLED | -1 |
| PORTRAIT | 0 |
| LANDSCAPE | 1 |
#### LayoutModeTypeObject
| Name | Value |
| --------- | ----- |
| UNHANDLED | -1 |
| FOCUSED | 0 |
| PIP | 1 |
| GRID | 2 |
#### OAuthScopes
| Value |
| ---------------------------- |
| 'bot' |
| 'rpc' |
| 'identify' |
| 'connections' |
| 'email' |
| 'guilds' |
| 'guilds.join' |
| 'guilds.members.read' |
| 'gdm.join' |
| 'messages.read' |
| 'rpc.notifications.read' |
| 'rpc.voice.write' |
| 'rpc.voice.read' |
| 'rpc.activities.write' |
| 'webhook.incoming' |
| 'applications.commands' |
| 'applications.builds.upload' |
| 'applications.builds.read' |
| 'applications.store.update' |
| 'applications.entitlements' |
| 'relationships.read' |
| 'activities.read' |
| 'activities.write' |
| 'dm\_channels.read' |
#### RPCCloseCodes
| Name | Code |
| ------------------ | ---- |
| CLOSE\_NORMAL | 1000 |
| CLOSE\_UNSUPPORTED | 1003 |
| CLOSE\_ABNORMAL | 1006 |
| INVALID\_CLIENTID | 4000 |
| INVALID\_ORIGIN | 4001 |
| RATELIMITED | 4002 |
| TOKEN\_REVOKED | 4003 |
| INVALID\_VERSION | 4004 |
| INVALID\_ENCODING | 4005 |
#### SkuTypeObject
| Name | Value |
| ------------ | ----- |
| UNHANDLED | -1 |
| APPLICATION | 1 |
| DLC | 2 |
| CONSUMABLE | 3 |
| BUNDLE | 4 |
| SUBSCRIPTION | 5 |
#### Relationship Types
| Value | Name | Description |
| ----- | ---------------- | ------------------------------------------------------------------------------------------------ |
| 0 | None | The user has no relationship with the other user. |
| 1 | Friend | The user is friends with the other user. |
| 2 | Blocked | The current user has blocked the target user. |
| 3 | Pending Incoming | The current user has received a friend request from the target user, but it is not yet accepted. |
| 4 | Pending Outgoing | The current user has sent a friend request to the target user, but it is not yet accepted. |
| 5 | Implicit | The Implicit type is documented for visibility, but should be unused in the SDK. |
| 6 | Suggestion | The Suggestion type is documented for visibility, but should be unused in the SDK. |
# Social SDK Core Concepts
Source: https://docs.discord.com/developers/discord-social-sdk/core-concepts
Overview of the Discord Social SDK's core concepts including features, communication, and platform compatibility.
The Discord Social SDK allows you to build social features into your game, including friend lists, messaging, voice chat, and rich presence. Unlike a traditional SDK with built-in UI components, the Discord Social SDK provides access to raw data, allowing developers to create a fully customized experience that aligns with their game's aesthetic.
## Core Concepts Overview
Select a topic below to learn more about the Discord Social SDK:
}>
Explore social features like account linking, friends, and rich presence.
}>
Learn about messaging, voice chat, and in-game lobbies.
}>
Learn about the integration overview and implementation steps.
}>
See supported platforms and download information.
}>
Understand OAuth2 scopes and client types for authentication.
***
## Next Steps
After exploring these core concepts, you can start implementing the Discord Social SDK in your game:
* **Ready to integrate?** Follow our [Getting Started](/developers/discord-social-sdk/getting-started) guide for step-by-step setup instructions.
* **Need implementation details?** Check out the development guides for specific features like [Account Linking](/developers/discord-social-sdk/development-guides/account-linking-with-discord) and [Managing Lobbies](/developers/discord-social-sdk/development-guides/managing-lobbies).
* **Looking for UI guidance?** Review the [Design Principles](/developers/discord-social-sdk/design-guidelines/principles) for best practices.
***
## Change Log
| Date | Changes |
| -------------- | ------------------------- |
| July 21, 2025 | restructure core concepts |
| June 30, 2025 | restructure oauth scopes |
| March 17, 2025 | initial release |
# Communication Features
Source: https://docs.discord.com/developers/discord-social-sdk/core-concepts/communication-features
Learn about Discord Social SDK's communication features including direct messages and voice chat.
The Discord Social SDK provides powerful communication features to enhance player interaction in your game.
These features include messaging, voice chat, and in-game lobbies, allowing players to connect seamlessly.
The following communication features are available for development and testing, but their usage is capped with a rate limit.
For more information about current rate limits for communication features, please see
[Rate Limits](/developers/discord-social-sdk/core-concepts/communication-features#rate-limits), and
see the
[Applying for Increased Rate Limits for Production Releases](/developers/discord-social-sdk/core-concepts/communication-features#applying-for-increased-rate-limits-for-production-releases)
section below for how to increase those rate limits, such that you can use these features for full release.
## Messaging & Communication
Users can communicate via direct messages (DMs) and voice calls:
* DMs: One-on-one private chat ([`MessageHandle`]).
* Calls: Real-time voice communication inside a game lobby ([`Call`]).
| Development Guides |
| ---------------------------------------------------------------------------------------------------- |
| [Sending Direct Messages](/developers/discord-social-sdk/development-guides/sending-direct-messages) |
| [Managing Voice Chat](/developers/discord-social-sdk/development-guides/managing-voice-chat) |
| Design Guidelines |
| ----------------------------------------------------------------------------------- |
| [Direct Messages](/developers/discord-social-sdk/design-guidelines/direct-messages) |
## Lobbies & In-Game Chat
A lobby is a virtual space where players can interact through voice and text chat.
* Your game controls lobbies, which can have different membership rules.
* Integrated voice chat allows real-time communication among players in a session.
| Development Guides |
| ------------------------------------------------------------------------------------- |
| [Manging Lobbies](/developers/discord-social-sdk/development-guides/managing-lobbies) |
## Linked Channels
Games can link in-game chat with Discord's server-based text channels in their UI, allowing players to chat in a Discord server without leaving the game.
| Development Guides |
| ------------------------------------------------------------------------------------ |
| [Linked channels](/developers/discord-social-sdk/development-guides/linked-channels) |
| Design Guidelines |
| ----------------------------------------------------------------------------------- |
| [Linked channels](/developers/discord-social-sdk/design-guidelines/linked-channels) |
***
## Development Rate Limits
Prior to successful approval to increase rate limits, communication features have the following restrictions:
* **Lobby Create/Join operations:** 100 requests every 2 hours.
* **Lobby Update operations:** 100 requests every 2 hours.
* This includes adding/removing members, updating metadata, and creating Discord server invites for linked channels.
* **Lobby Linking operations:** 20 requests every 2 hours.
* This includes creating or deleting a linked channel
* **Sending Messages to Lobby operations:** 100 requests every 2 hours
* **Sending Direct Message operations:** 100 requests every 2 hours.
These are per-application rate limits, not per-user.
These limitations are designed to provide sufficient capacity for development, testing, and small-scale demos while ensuring system stability. Once you gain full access through the rate limit increase application process, these restrictions are increased, allowing your game to scale to production levels.
## Applying for Increased Rate Limits for Production Releases
To apply for increased rate limits on Discord Social SDK communication features, you must meet the requirements
outlined in the [Application Requirements for Increased Rate Limits](/developers/discord-social-sdk/core-concepts/communication-features#application-requirements-for-increased-rate-limits)
section below.
Once you have met those requirements, you can apply for increased rate limits by following these steps:
1. Open the [Application page of the Developer Portal](https://discord.com/developers/applications)
2. Open the Application you want to apply for rate limit increase
3. Under the **Discord Social SDK** heading in the sidebar, click the [**Comms Access**](https://discord.com/developers/applications/select/social-sdk/comms-access) button
4. Fill out the application form with the required information and supporting materials
Video capture provided to the application should be in url format, hosted on a file or video-sharing service of your choice as an unlisted video
## Application Requirements for Increased Rate Limits
This section outlines the specific requirements you must meet before submitting your game for approval to unlock
Discord Social SDK text and/or voice communications for full release. You'll find:
1. **Minimum required feature set:** Your game must have integrated core Discord Social SDK features, such as Discord Account Linking, Rich Presence with Discord Joins, and Full Access to Discord Friends.
2. **Feature Functionality:** The Discord Social SDK features you choose to integrate in your game must have end-to-end functionality, meaning that all your user interactions can be completed, work as intended across both game and Discord clients, and resolve negative options (such as user denial of options and failure states) correctly.
3. **Feature Access and Conveyance:** Discord Social SDK integrations must include the option for users to link their Discord account, and make it accessible and easily understandable for players to activate.
4. **Supporting Materials:** You’ll be required to provide specific documentation of your game for review, such as a capture of integration and development timeline.
5. **Age-restricted user protection:** You’ll need to confirm that you have appropriate measures in place for players’ access to the SDK features in your game, depending on their age.
These requirements detail the **minimum bar** to be eligible for Social SDK communications usage at scale.
Check out our [design guidelines](/developers/discord-social-sdk/design-guidelines) for what **“great”** looks like. Great
integrations can drive higher player engagement and retention by creating seamless social experiences that keep players
connected and coming back to your game. And of course, remember to make sure you are complying with our
[Social SDK Terms](https://support-dev.discord.com/hc/en-us/articles/30225844245271-Discord-Social-SDK-Terms).
If you believe you meet these requirements, you can
[apply for access to full release comms features](/developers/discord-social-sdk/core-concepts/communication-features#applying-for-increased-rate-limits-for-production-releases).
Discord may approve or deny your access after review of your application and additional terms may apply.
### Minimum Required Feature Set
**In order to unlock Discord text and/or voice communications for full release, integrations of the Social SDK must include the following features at a minimum**. Developers are of course welcome (and encouraged) to take advantage of any SDK features that will supercharge their games beyond these minimum requirements.
Exception: Prohibited Off-Platform Interactions by Sony
For integrations experienced on Sony Platforms (e.g. PlayStation 5)…
1. **Do not allow access to Account Linking** entry points.
2. **Do not allow access to Linked Channels**.
3. **Limit messaging and Invites to other players currently playing** the game.
4. **Do not display off-platform presence and logos** (including Discord).
Required features are…
#### Option for Discord Account Linking
**Players must have the opportunity to [link their Discord account in-game](/developers/discord-social-sdk/design-guidelines/signing-in).** This ensures players can access the full Social SDK-powered experience, and connects your game to a player’s existing social network.
#### Rich Presence and Discord Joins
**Discord Users must be able to see [Rich Presence](/developers/discord-social-sdk/design-guidelines/status-rich-presence) updates for Discord-linked player accounts and join their game via Ask to Join.** This feature drives organic discovery and social engagement by showing friends what players are doing and creating opportunities for spontaneous social interaction and game sessions.
#### Full Access to Discord Friends
**Account-linked players must have their Discord friends accessible to them in-game, with supporting UI to take actions like [messaging](/developers/discord-social-sdk/design-guidelines/direct-messages), inviting, blocking, and ignoring**. This can be accomplished as a [Unified Friends List](/developers/discord-social-sdk/design-guidelines/unified-friends-list) which shows status across platforms, or as a simple in-game Discord friends list. Games should never copy and store Discord friend information (i.e. a player unlinking their Discord account should automatically remove Discord friend information in-game).
### Feature Functionality
**All integrated Social SDK features (whether required as a part of the minimum feature set or not) must have at a minimum base level functionality for their intended use.** This is a **separate requirement in addition to the minimum feature set** above. “Base level functionality” means that each integrated feature’s **end-to-end user flow is** **completable** and **resolves negative options** (user denial of options and failure states) correctly. Details for each feature are as follows:
#### Discord Account Linking
**Intended Use:** Connect a player's game account with their Discord account to enable social features and cross-platform functionality.
**Example Complete User Flow:**
1. Player encounters sign-in prompt (at game start, friends list access, or feature interaction)
2. Player clicks Discord sign-in button
3. Authorization flow launches (Overlay > Discord Client > Browser fallback)
4. Player completes OAuth authorization on Discord
5. Game receives authorization confirmation
6. Success state displays in-game confirming connection
7. Social features become available and populate with Discord data
**Negative Options to Resolve:**
* **User declines authorization:** Return to game normally with a provisional account; maintain access to sign-in option
* **Authorization fails/times out:** Show clear error message with retry option
* **User wants to disconnect:** Provide deauthorization option in-game and maintain game functionality post-disconnect
These requirements represent the minimum standards for functionality.
To create an even better player experience, see our [Design Guidelines for Signing In](/developers/discord-social-sdk/design-guidelines/signing-in).
#### Rich Presence
**Intended Use:** Display detailed, real-time information about a player's in-game activity on their Discord profile, allowing friends to see what they're doing and potentially join their session.
**Example Complete User Flow:**
1. Player launches game and performs actions (enters lobby, starts match, joins party, etc.)
2. Game feeds Rich Presence update to Discord via the SDK (e.g. “In lobby”)
3. Rich Presence appears on player's Discord profile with current activity details
4. Discord friends can view the presence information
5. If including Joins/Invites, Discord Friends can "Ask to Join" or receive direct invites
6. Game feeds additional Rich Presence updates to Discord via the SDK as the player's in-game status changes (e.g. “Looking for a match”, “In a match”, etc)
7. On game shutdown, Discord automatically handles clearing player's Rich Presence
**Negative Options to Resolve:**
* **Network connectivity issues:** Queue presence updates and retry when connection restored
* **Active and inactive clients**: Invites can complete even when one player doesn’t have the game client open on acceptance.
These requirements represent the minimum standards for functionality.
To create an even better player experience, see our [Design Guidelines for Rich Presence](/developers/discord-social-sdk/design-guidelines/status-rich-presence).
#### Unified Friends List
**Intended Use:** Combine Discord friends and in-game relationships into a single, organized friends list that shows comprehensive social connections.
**Example Complete User Flow:**
1. Player opens their in-game friends list
2. Game UI quickly populates friends in an organized list showing:
* In-game friends (always visible)
* "Online Elsewhere" section with Discord friends not currently in-game
* Appropriate status indicators and Discord badges
3. Player interacts with friends in list (message, invite, block, view profile)
4. Friends list updates dynamically in real time as statuses change
5. Player can add/remove friends through the interface
**Negative Options to Resolve:**
* **Friend removal:** Handle removal across both game and Discord systems appropriately
* **Loading failures:** Show loading states and error messages for failed friend data retrieval
* **Special characters**: Non-standard characters in Discord usernames populate as expected.
* **Unlinking Discord**: Player’s in-game friends list removes Discord friend entries when they unlink their Discord account
These requirements represent the minimum standards for functionality.
To create an even better player experience, see our [Design Guidelines for Unified Friends List](/developers/discord-social-sdk/design-guidelines/unified-friends-list).
#### Direct Messages through Discord
**Intended Use:** Enable seamless messaging between players across game platforms through Discord, maintaining conversation continuity.
**Complete User Flow:**
1. Player selects friend to message from an in-game friends list or other interface
2. Message interface opens showing chat history (if available)
3. Player composes and sends message
4. Message appears in both the game interface and Discord (for linked users)
5. Messages from Discord appear in-game with Discord visual notation
6. Conversation continues seamlessly across platforms
**Negative Options to Resolve:**
* **Message delivery failure:** Show retry options and clear error states
* **Unsupported media:** Display placeholder with external link icon for rich media
* **Blocked users:** Respect Discord blocking relationships and show appropriate messaging
* **Network issues:** Queue messages locally and sync when connection restored
These requirements represent the minimum standards for functionality.
To create an even better player experience, see our [Design Guidelines for Direct Messages](/developers/discord-social-sdk/design-guidelines/direct-messages).
#### Linked Channels
**Intended Use:** Link in-game group chats to Discord text channels, enabling persistent conversations that flow between game platforms through Discord.
**Complete User Flow:**
1. Player with Discord server admin & game chat admin permissions accesses channel linking option in group chat interface
2. Channel selection interface opens showing available Discord servers/channels
3. Player selects appropriate text channel (with proper permissions)
4. System shows setup warning if linking to restricted channel
5. Link establishes successfully with confirmation
6. Messages flow bidirectionally between game and Discord
7. Persistent entrypoint shows linked channel info and management options
**Negative Options to Resolve:**
* **Insufficient permissions:** Show clear error about channel management requirements
* **No available channels:** Provide guidance on creating or accessing appropriate channels
* **Link establishment failure:** Show retry options and clear error messaging
* **User wants to unlink:** Provide confirmation dialog and clean removal process
These requirements represent the minimum standards for functionality.
To create an even better player experience, see our [Design Guidelines for Linked Channels](/developers/discord-social-sdk/design-guidelines/linked-channels).
#### Voice Communications
**Intended Use:** Provide high-quality voice chat integration that works seamlessly between game platforms through Discord.
**Complete User Flow:**
1. Player joins voice-enabled game session/party
2. Voice interface becomes available with Discord-powered audio
3. Player can mute/unmute, adjust volume, see speaking indicators
4. Voice streams work for individual users with clear audio quality
5. Player can leave voice
**Negative Options to Resolve:**
* **Voice server unavailable:** Provide clear error messaging and retry mechanisms
* **User leaves voice:** Clean up voice states and update interfaces appropriately
### Feature Access and Conveyance
#### Discord Account Linking
**The option for players to link their Discord account must have top-level surfacing in-game.** Social SDK integrations must not only include account linking, but also make it accessible and easily understandable for players to activate and deactivate. That means…
**Access Requirements:**
* Linking a Discord account is surfaced in parity with other platform-linking options.
* The location for account linking is located in a **persistent and accessible** area in-game (e.g. Game Settings, Accounts menus).
* The entry point for account linking is visually persistent within its associated menu location (i.e. it can’t be a one time pop up that players can’t access again).
* The entry point for account linking is discoverable by players through normal expected play (i.e. not buried in a sub-menu or requiring right-click access).
* The option to unlink accounts is also surfaced in-game in a persistent and easily accessible manner.
* Users without linked accounts must have an associated token path for handling social features (see: [Provisional Accounts](/developers/discord-social-sdk/design-guidelines/provisional-accounts)).
**Conveyance Requirements:**
* Developers must provide contextual education about Discord account-linking benefits before users encounter the authorization flow.
* Acceptable contextual education looks like:
* **Modal or informational screen** that explains specific benefits the player will receive (e.g., "Chat with friends here and on Discord," "See what your friends are playing," "Join friends' games directly")
* **Contextual tooltips or hints** near social features that explain how they improve with Discord linking
* **In-game messaging** that appears when players interact with limited social features, explaining how linking would enhance the experience
Discord Account Linking entry points are prohibited on Sony platforms.
#### Direct Messages through Discord
If you’ve chosen to integrate Direct Messages through Discord in your game, **Direct Messages must be identifiable as coming from Discord,** and **players must know when their messages are showing up in Discord.**
**Conveyance Requirements:**
* Cross-platform DMs need to be identified as Discord-specific through visual indicators (e.g. Discord logo/badge next to sender name, contextual information on hover about the message source)
* Players must know when their messages are showing up in Discord through clear visual feedback when composing or sending messages
* The messaging interface must indicate the cross-platform nature of the conversation (e.g., "This conversation syncs with Discord" or similar messaging)
### Supporting Materials
To **unlock Discord text and/or voice communications for full release,** Social SDK integrations must have accompanying
supporting materials in your application. You may submit your application and supporting materials under your application
in the Developer Portal.
**For integrations available on multiple platforms including Sony platforms, submit materials that reflect your non-Sony platform user experience.**
**For example:** Example Studio's *Wumpus Wars* is available to players on Sony PlayStation 5 and Xbox
Series S and Xbox Series X. Example Studio submits the feature list and *Wumpus Wars* capture for Xbox.
#### List of SDK Features Integrated
Provide a comprehensive list of all Discord Social SDK features implemented in your game, such as:
* Discord Sign-in (Account Linking)
* Rich Presence
* Game Invites and/or Discord Joins
* Unified Friends List
* Direct Messages through Discord
* Voice Communications
* Text Communications (Linked Channels)
#### Release Timeline
Indicate the anticipated release date, and the current development status of your Discord integration:
* Concept / Prototype
* Production (Alpha)
* Post-product (Beta)
* Released (Already commercially available)
#### Capture of Integrated Features
Acceptable format: url link
* **1-5 minute video capture** of the complete user flow covering each SDK feature integrated
* Videos must demonstrate end-to-end user flow as outlined in
[Feature Functionality](/developers/discord-social-sdk/core-concepts/communication-features#feature-functionality)
* Show both successful flows and how negative options (errors, user denials) are handled
* Include clear narration or on-screen text explaining each step
* Ensure video quality allows reviewers to clearly see UI elements and user interactions
### Age-restricted User Protection
You must comply with all age requirements under our [Social SDK Terms](https://support-dev.discord.com/hc/en-us/articles/30225844245271-Discord-Social-SDK-Terms). This includes ensuring that only people who are at least 13 years old and meet the minimum age requirements in their country can access Social SDK features integrated in your game.
### Confirmation of Standards Compliance
With your application submission, **you confirm that you have met the requirements detailed above**.
## Next Steps
Learn about integration steps, check platform support, and explore implementation guides:
}>
Learn about the integration overview and implementation steps.
}>
See supported platforms and download information.
}>
Learn how to implement messaging, voice chat, and linked channels.
***
## Change Log
| Date | Changes |
| ------------- | --------------- |
| July 21, 2025 | initial release |
[`Call`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#a1cc8a7f73c15a960bc409d734b5edbd1
[`MessageHandle`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#ae25595b43bc74b0c4c92c5165d16382f
# Core Features
Source: https://docs.discord.com/developers/discord-social-sdk/core-concepts/core-features
Overview of the Discord Social SDK's core features including user identity and relationships.
The Discord Social SDK offers a range of features to enhance social interactions within games. Developers can leverage these features to create a more engaging and connected experience for players in their game.
## Account Linking
Account linking allows a game to authenticate users with their Discord credentials, gaining access to social features like friends, chat, and presence. This process uses OAuth2 authentication.
Check out our [Account Linking](/developers/platform/account-linking) platform overview for a detailed explanation of how account linking works, why it matters, and how to implement it in your game.
| Development Guides |
| --------------------------------------------------------------------------------------------------------------------------- |
| [Account Linking from Your Game](/developers/discord-social-sdk/development-guides/account-linking-with-discord) |
| [Account Linking on Mobile](/developers/discord-social-sdk/development-guides/account-linking-on-mobile) |
| [Account Linking on Consoles](/developers/discord-social-sdk/development-guides/account-linking-on-consoles) |
| [Account Linking from Discord Entry Points](/developers/discord-social-sdk/development-guides/account-linking-from-discord) |
| Design Guidelines |
| -------------------------------------------------------------------------------------- |
| [Signing in with Discord](/developers/discord-social-sdk/design-guidelines/signing-in) |
| [Consoles](/developers/discord-social-sdk/design-guidelines/consoles) |
## Provisional Accounts
Provisional accounts let players use social features in your game without linking a Discord account so all players can have a consistent gameplay experience.
| Development Guides |
| ---------------------------------------------------------------------------------------------------------- |
| [Using Provisional Accounts](/developers/discord-social-sdk/development-guides/using-provisional-accounts) |
| Design Guidelines |
| --------------------------------------------------------------------------------------------- |
| [Provisional Accounts](/developers/discord-social-sdk/design-guidelines/provisional-accounts) |
## Friend System & Relationships
The SDK models friendships and relationships in two ways:
* Discord Friends: Persistent across all games.
* Game-Specific Friends: Limited to the current game.
| Development Guides |
| -------------------------------------------------------------------------------------------------------------------- |
| [Creating a Unified Friends List](/developers/discord-social-sdk/development-guides/creating-a-unified-friends-list) |
| Design Guidelines |
| --------------------------------------------------------------------------------------------- |
| [Unified Friends List](/developers/discord-social-sdk/design-guidelines/unified-friends-list) |
| [Game Friends](/developers/discord-social-sdk/design-guidelines/game-friends) |
## Presence & Rich Presence
Presence refers to a user's online status, while Rich Presence provides game-specific activity data:
* Displays if a user is online, idle, or offline.
* Shows detailed game stats (e.g., what level they're playing and time played).
* Allows users to send game invites through Discord and in-game.
| Development Guides |
| ------------------------------------------------------------------------------------------------ |
| [Setting Rich Presence](/developers/discord-social-sdk/development-guides/setting-rich-presence) |
| Design Guidelines |
| ----------------------------------------------------------------------------------------------- |
| [Status & Rich Presence](/developers/discord-social-sdk/design-guidelines/status-rich-presence) |
***
## Next Steps
Learn the implementation steps, configure authentication, and explore development guides:
}>
Learn about messaging, voice chat, and in-game lobbies.
}>
Learn about the integration overview and implementation steps.
}>
Step-by-step guides for implementing each social feature.
***
## Change Log
| Date | Changes |
| ------------- | --------------- |
| July 21, 2025 | initial release |
# Integration Overview
Source: https://docs.discord.com/developers/discord-social-sdk/core-concepts/integration-overview
High-level overview of integrating the Discord Social SDK into your application.
To implement the Discord Social SDK, developers for all platforms will generally follow these steps:
1. Import the SDK
2. Initialize the SDK:
* Create a [`Client`] instance.
* Set up event listeners to monitor SDK events and callbacks
3. Authenticate users with flexible account options
* Link an existing Discord account via OAuth ([`Client::Authorize`]).
* Create and manage provisional accounts for users who don't have or want a Discord account ([`Client::GetProvisionalToken`]).
4. Implement social features
* Implement [unified friends list](/developers/discord-social-sdk/development-guides/creating-a-unified-friends-list) and [relationships](/developers/discord-social-sdk/development-guides/managing-relationships).
* Use [rich presence](/developers/discord-social-sdk/development-guides/setting-rich-presence) for game activity updates.
* Set up [lobbies](/developers/discord-social-sdk/development-guides/managing-lobbies) for multiplayer interaction and [game invites](/developers/discord-social-sdk/development-guides/managing-game-invites).
* Manage [direct message](/developers/discord-social-sdk/development-guides/sending-direct-messages), [linked channels](/developers/discord-social-sdk/development-guides/creating-a-unified-friends-list), and [voice communication](/developers/discord-social-sdk/development-guides/managing-voice-chat).
5. Handle events & API calls:
* Listen for changes in friend lists, presence updates, and chat messages.
* Use Discord's APIs to update statuses, send messages, and manage connections.
This guide is a conceptual overview. If you're ready to start building, [follow our step-by-step guide](/developers/discord-social-sdk/getting-started) to set up the SDK in your game engine.
***
## Next Steps
Check platform compatibility, explore communication features, and start your integration:
}>
See supported platforms and download information.
}>
Learn about messaging, voice chat, and in-game lobbies.
}>
Choose your platform and follow step-by-step setup instructions.
***
## Change Log
| Date | Changes |
| ------------- | --------------- |
| July 21, 2025 | initial release |
[`Client`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a91716140c699d8ef0bdf6bfd7ee0ae13
[`Client::Authorize`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ace94a58e27545a933d79db32b387a468
[`Client::GetProvisionalToken`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a8003130b6c46e54ac68442483bf0480c
# Discord Social SDK on Mobile
Source: https://docs.discord.com/developers/discord-social-sdk/core-concepts/mobile
Add the Discord Social SDK to your iOS and Android games.
This guide explains how to add the Discord Social SDK to your iOS and Android game. It covers what's supported on mobile, the mobile-specific setup you'll need, and where to go next for each game engine.
***
## Platform Support
The Discord Social SDK is **Generally Available** on both mobile platforms:
| Platform | Supported Versions | Standalone C++ | Unreal Engine | Unity |
| -------- | ------------------ | -------------- | ------------- | ----- |
| Android | 7.0+ | ✅ | ✅ | ✅ |
| iOS | 15.1+ | ✅ | ✅ | ✅ |
For the full cross-platform support matrix, see [Platform Compatibility](/developers/discord-social-sdk/core-concepts/platform-compatibility).
***
## Feature Availability on Mobile
All Discord Social SDK features are available on mobile. The table below lists mobile-specific setup or behavior differences where they apply.
| Feature | Mobile-Specific Notes |
| ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| [Account Linking](/developers/discord-social-sdk/development-guides/account-linking-with-discord) | Requires deep linking (`discord-APP_ID:/authorize/callback`). Discord mobile app launches first for authentication, falls back to browser if not installed. See [Account Linking on Mobile](/developers/discord-social-sdk/development-guides/account-linking-on-mobile) |
| [Provisional Accounts](/developers/discord-social-sdk/development-guides/using-provisional-accounts) | No special considerations |
| [Friends & Relationships](/developers/discord-social-sdk/development-guides/managing-relationships) | No special considerations |
| [Rich Presence](/developers/discord-social-sdk/development-guides/setting-rich-presence) | Requires account linking. Unlike desktop, you cannot publish presence on mobile without account linking first |
| [Game Invites](/developers/discord-social-sdk/development-guides/managing-game-invites) | Requires https URL configured in Developer Portal General tab and specifying supported mobile platforms. See [Supporting Mobile Game Invites](/developers/discord-social-sdk/development-guides/managing-game-invites#supporting-mobile-game-invites) |
| [Direct Messages](/developers/discord-social-sdk/development-guides/sending-direct-messages) | No special considerations |
| [Lobbies & In-Game Chat](/developers/discord-social-sdk/development-guides/managing-lobbies) | No special considerations |
| [Voice Chat](/developers/discord-social-sdk/development-guides/managing-voice-chat) | Request microphone permissions. See platform-specific setup: [iOS](/developers/discord-social-sdk/development-guides/account-linking-on-mobile#2-set-microphone-usage-description), [Android](/developers/discord-social-sdk/development-guides/account-linking-on-mobile#3-android-permissions) |
| [Linked Channels](/developers/discord-social-sdk/development-guides/linked-channels) | No special considerations |
***
## Prerequisites
Before integrating on mobile, make sure you have:
* A Discord application with the Social SDK enabled. See [Getting Started](/developers/discord-social-sdk/getting-started) to create your team, application, and enable the SDK.
* A mobile OAuth2 redirect URI configured in the format `discord-YOUR_APP_ID:/authorize/callback` (replace `YOUR_APP_ID` with your Discord application ID).
* Your development environment set up for your target platform (Xcode for iOS, Android Studio for Android).
**Reduce binary size:** You can reduce the SDK's installation size on mobile by excluding Krisp noise cancellation. See [Excluding Krisp to Reduce Installation Size](/developers/discord-social-sdk/development-guides/managing-voice-chat#excluding-krisp-to-reduce-installation-size).
***
## Choose Your Getting Started Path
Select the integration that matches your project. Each guide covers download, project setup, and connecting to Discord, with mobile-specific notes where they apply.
}>
For custom engines or standalone applications targeting iOS and Android.
}>
For Unity projects building to mobile.
}>
For Unreal Engine projects building to mobile.
***
## Samples
### Unity Mobile Sample
Check out our [Unity sample for the Discord Social SDK](https://github.com/discord/social-sdk-unity-sample), which includes a mobile scene (`Assets/Scenes/Mobile.unity`) that runs on Android and iOS. It contains easy to drop in prefabs with both code and UI to quickly integrate Discord's social features into your mobile game, with all mobile-specific configuration already set up:
* Mobile redirect URI (`discord-APP_ID:/authorize/callback`) registration
* Build processors that auto-inject the redirect scheme into Android manifest and iOS `Info.plist`
* Portrait-optimized UI that reuses the same Discord SDK integration as the desktop scene
See the **[Mobile Setup](https://github.com/discord/social-sdk-unity-sample#mobile-setup)** section in the sample's README for build instructions.
***
## Next Steps
}>
UI patterns and principles for integrating Discord into your game.
}>
Add features like a unified friends list, rich presence, and more.
***
## Change Log
| Date | Changes |
| ------------- | --------------- |
| July 14, 2026 | Initial release |
# OAuth2 Scopes for the Social SDK
Source: https://docs.discord.com/developers/discord-social-sdk/core-concepts/oauth2-scopes
Learn about OAuth2 scopes required for Discord Social SDK features.
This page covers OAuth2 scopes specific to the Discord Social SDK. For the full list of Discord API OAuth2 scopes (bots, webhooks, etc.), see the [OAuth2 topic guide](/developers/topics/oauth2#shared-resources-oauth2-scopes).
OAuth2 scopes define the level of access your app has to a user's Discord account.
What OAuth scopes are available to your integration are set via
[`AuthorizationArgs::SetScopes`] on [`AuthorizationArgs`] which is passed to [`Client::Authorize`] on Social SDK
authentication.
## Default Presence Scopes
At a minimum, the Social SDK uses the following scopes to use features like rich presence and friends list:
* `openid`
* `sdk.social_layer_presence`
The default presence features include:
* [Account Linking](/developers/discord-social-sdk/core-concepts/core-features#account-linking)
* [Provisional Accounts](/developers/discord-social-sdk/core-concepts/core-features#provisional-accounts)
* [Friend System & Relationships](/developers/discord-social-sdk/core-concepts/core-features#friend-system-relationships)
* [Presence & Rich Presence](/developers/discord-social-sdk/core-concepts/core-features#presence-rich-presence)
The Social SDK provides the helper method [`Client::GetDefaultPresenceScopes`], which returns `openid sdk.social_layer_presence`,
that you can use when setting up your OAuth2 flow, for integrations that only need the above functionality.
With only the default presence scopes, your game will not be able to use any of the limited access
communications features.
## Default Communication Scopes
The communications features are currently available but have limited access. Those features **require** the scope
of `sdk.social_layer`, which includes the `sdk.social_layer_presence` scope but also allows your app to use those limited features on behalf of the user.
* `openid`
* `sdk.social_layer`
These communication features include:
* [Messaging & Communication](/developers/discord-social-sdk/core-concepts/communication-features#messaging-communication)
* [Lobbies & In-Game Chat](/developers/discord-social-sdk/core-concepts/communication-features#lobbies-ingame-chat)
* [Linked Channels](/developers/discord-social-sdk/core-concepts/communication-features#linked-channels)
The Social SDK provides the helper method [`Client::GetDefaultCommunicationScopes`], which returns `openid sdk.social_layer`,
that you can use when setting up your OAuth2 flow, for integrations that integrates both the default and limited communications features.
For more information about these features, please see [Core Concepts: Communication Features](/developers/discord-social-sdk/core-concepts/communication-features).
If your game requires additional scopes, you can add them to the default scopes to authorize additional access from your users.
You should only add scopes that are necessary for your game to function. Requesting unnecessary scopes can lead to user distrust and may result in users not linking their Discord account.
## OAuth2 Client Types
OAuth2 has two client types: **Confidential** and **Public**. Most games will not want to ship with **Public Client** enabled.
Some Social SDK methods require your Discord application to be a **Public Client**. These methods also have server-side alternatives that you can use with a **Confidential Client**.
* Using confidential clients with proper secret management for production applications is generally recommended.
* Public clients cannot securely store client secrets.
* Your security team should review this setting and authentication flows before releasing your game.
[Learn more about OAuth2 client types](https://oauth.net/2/client-types)
***
## Next Steps
Start your integration, implement user authentication, and learn about UI design:
}>
Choose your platform and follow step-by-step setup instructions.
}>
Implement user authentication with Discord OAuth2.
}>
Learn how to design your game's UI to integrate social features.
***
## Change Log
| Date | Changes |
| ------------- | --------------- |
| July 21, 2025 | initial release |
[`AuthorizationArgs`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationArgs.html#adb47ac55258db29d4cb8a2c506093eed
[`AuthorizationArgs::SetScopes`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationArgs.html#aa3714d11a196e0d71c8c1cf38c506d92
[`Client::Authorize`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ace94a58e27545a933d79db32b387a468
[`Client::GetDefaultCommunicationScopes`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a71499da752fbdc2d4326ae0fd36c0dd1
[`Client::GetDefaultPresenceScopes`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a7648bd1d2f7d9a86ebd0edb8bef12b5c
# Platform Compatibility
Source: https://docs.discord.com/developers/discord-social-sdk/core-concepts/platform-compatibility
Learn about Discord Social SDK platform support and compatibility.
You can find instructions on how to download the SDK for each platform in the [Getting Started](/developers/discord-social-sdk/getting-started) guide.
***
## Game Engine and Language
The Discord Social SDK supports the following game engines and language environments:
| Integration | Supported Version |
| -------------- | ----------------- |
| Standalone C++ | C++20+ |
| Unity | 2021.3+ |
| Unreal Engine | 5.5+ |
**PlayStation 4 / PS5 exception:** If you're targeting PlayStation 4, or PlayStation 5 with a PS5 SDK older than
13.000, the Discord Social SDK still supports C++17. All other platforms require C++20.
Prior to Social SDK release 1.9, C++17 is the minimum supported version.
***
## Platforms
The Discord Social SDK is available for the following platforms:
| Platform | Supported Versions | Support Level | Standalone C++ | Unreal Engine | Unity |
| ---------------- | ------------------ | ------------------- | -------------- | ------------- | ----- |
| **Desktop** | | | | | |
| Windows (x64) | 10+ | Generally Available | ✅ | ✅ | ✅ |
| Windows (ARM64) | 11+ | Generally Available | ✅ | ❌ | ❌ |
| macOS (x64) | 10.5+ | Generally Available | ✅ | ❌ | ✅ |
| macOS (ARM64) | 11+ | Generally Available | ✅ | ❌ | ✅ |
| Linux | \*glibc 2.31+ | Experimental | ✅ | ✅ | ✅ |
| **Mobile** | | | | | |
| Android | 7.0+ | Generally Available | ✅ | ✅ | ✅ |
| iOS | 15.1+ | Generally Available | ✅ | ✅ | ✅ |
| **Console** | | | | | |
| Xbox One | GDK 250400-260400 | Experimental | ✅ | ✅ | ❌ |
| Xbox Series X\|S | GDK 250400-260400 | Generally Available | ✅ | ✅ | ✅ |
| PlayStation 4 | OS 11.500-12.500 | Experimental | ✅ | ✅ | ❌ |
| PlayStation 5 | OS 11.000-13.000 | Generally Available | ✅ | ✅ | ✅ |
\* There are too many Linux distributions to test, but most distros with glibc 2.31, e.g. Ubuntu 20.04,
or later should work.
Building for iOS or Android? See [Feature Availability on Mobile](/developers/discord-social-sdk/core-concepts/mobile#feature-availability-on-mobile) for mobile-specific setup requirements and behavior differences.
Each Social SDK release supports roughly the last 18 months of Xbox and PlayStation OS SDK versions.
See [Console OS SDK Support](/developers/discord-social-sdk/core-concepts/release-cadence-and-support#console-os-sdk-support) for how the window rolls forward at each release.
To use the Discord Social SDK in your console games, you will need to request middleware approval and be an approved developer for the target console. Check out [this article](https://support-dev.discord.com/hc/en-us/articles/30209074764183) to learn more.
Exception: Prohibited Off-Platform Interactions by Sony
For integrations experienced on Sony Platforms (e.g. PlayStation 5)…
1. **Do not allow access to Account Linking** entry points.
2. **Do not allow access to Linked Channels**.
3. **Limit messaging and Invites to other players currently playing** the game.
4. **Do not display off-platform presence and logos** (including Discord).
***
## Next Steps
Configure authentication, explore features, and review our release cadence:
}>
Release cadence, supported version window, and end-of-life process.
}>
Explore social features like account linking, friends, and rich presence.
}>
Platform-specific setup instructions and download links.
***
## Change Log
| Date | Changes |
| ------------- | ------------------------------------------------------------------- |
| May 26, 2026 | added console minimum versions; linked to Release Cadence & Support |
| May 20, 2026 | Added Game Engine and Language section. |
| July 21, 2025 | initial release |
# Release Cadence & Support
Source: https://docs.discord.com/developers/discord-social-sdk/core-concepts/release-cadence-and-support
Discord Social SDK release cadence, supported version window, and end-of-life process.
This page describes which Discord Social SDK versions receive updates, our release cadence, and how end-of-life is handled. For platform support details, see [Platform Compatibility](/developers/discord-social-sdk/core-concepts/platform-compatibility).
These changes take effect with the **1.10 release** of the Discord Social SDK, shipping in **July 2026**. Starting with 1.10:
* The supported version window will cover versions **1.6 through 1.10**. Versions **1.5 and earlier** will be end-of-life.
* New Xbox and PlayStation OS SDK support will ship in new Social SDK releases only — not as patches to older Social SDK versions. Each Social SDK release bundles roughly the last 18 months of console OS SDK versions.
**Already-shipped Social SDK releases (1.6–1.9) are unchanged** — they keep the console OS SDK support they were originally built with. The new model applies from 1.10 forward.
We're announcing this ahead of the 1.10 release so developers on older versions have time to upgrade.
## Release Schedule
We ship **three minor releases of the Discord Social SDK each year**, in **April, July, and November**. This cadence aligns with the release windows for console OS SDKs so that each Discord Social SDK release can pick up the most recent supported console SDK versions.
Between minor releases, we publish patch releases as needed (see [What's Covered](#whats-covered) below).
## Supported Version Window
We provide **hotfix patches** — bug fixes and security fixes — for the **five most recent minor versions** of the Discord Social SDK. Older versions are End-of-Life (EOL) and will not receive further updates.
To receive a patch fix on an EOL version, upgrade to a supported release first.
For how console OS SDK updates work, see [Console OS SDK Support](#console-os-sdk-support) below.
### Supported Versions
Once the 1.10 release ships in July 2026, the supported version window will be:
| Version | Status |
| --------------- | ------------------- |
| 1.10 | Supported — current |
| 1.9 | Supported |
| 1.8 | Supported |
| 1.7 | Supported |
| 1.6 | Supported |
| 1.5 and earlier | End-of-Life |
## Console OS SDK Support
New Xbox and PlayStation OS SDK support ships in **new minor Social SDK releases only** (the three minor releases per year). They are not backported to older Social SDK versions as patches.
Each Social SDK release bundles roughly the last **18 months** of console OS SDK versions — from whatever Xbox GDK or PlayStation OS was current \~18 months before the release, up through the latest available at release time. Console OS SDK versions older than that window are dropped at each new Social SDK release.
In practice, this means:
* If you need the latest Xbox or PlayStation platform SDK, upgrade to the latest Social SDK release.
* Already-shipped Social SDK releases continue to support the console OS SDK versions they were originally built with — we don't add or remove support for console OS SDKs from already-shipped versions.
The minimum supported Xbox and PlayStation platform SDK versions for the current Social SDK release are listed in [Platform Compatibility](/developers/discord-social-sdk/core-concepts/platform-compatibility).
## End-of-Life Process
When a new minor version of the Social SDK is released, the oldest version in the support window rolls off and reaches End-of-Life:
1. It is removed from the supported version table above
2. No new hotfix patch builds are produced for that version
3. Developers on that version should upgrade to the latest supported version
EOL transitions are announced in the [Change Log](/developers/change-log) entry for the corresponding minor release.
## What's Covered
Within the supported Social SDK version window, hotfix patches for a minor version may include:
* Bug-fix and stability hotfix patches
* Security hotfix patches
## What's Not Covered
The following are **not** provided for older minor versions of the Social SDK:
* New feature development
* Backports of features introduced in a newer minor version
* Backports of support for newer console OS SDK versions
***
## Next Steps
}>
See supported platforms and minimum supported versions.
}>
Browse release notes and EOL announcements.
}>
Download the latest supported SDK version.
***
## Change Log
| Date | Changes |
| ------------ | --------------- |
| May 26, 2026 | initial release |
# Branding Guidelines
Source: https://docs.discord.com/developers/discord-social-sdk/design-guidelines/branding-guidelines
Official branding guidelines for using Discord's brand assets in your integration.
## Discord Logo
Download the Discord word-mark assets [discord.com/branding](https://discord.com/branding).
Please do not edit, change, distort, recolor, or reconfigure the Discord logo.
## Buttons
There are four sets of button styles for this integration: Blurple, Light, Dark, and Flexible.
#### Flexible
The Flexible buttons are intended to be restyled to match the game's aesthetic (examples). Flexible buttons include the font of the text before the logo as well as the button colors. The Discord logo and its colors should not be altered. This set is for in-game connection points.
#### Blurple / Light / Dark
These pre-styled buttons leverage colors vetted by our Design Systems team to pass contrast ratios across different backgrounds.
## Spacing
Ensure that there is adequate spacing between button text and the Discord logo as well as the left/right edges of the button.
When using the Flexible button styling, ensure that your typeface's baseline and x-height are aligned to the Discord logo.
## Resources
* [Discord branding guidelines](https://discord.com/branding)
## Change Log
| Date | Changes |
| -------------- | --------------- |
| March 17, 2025 | Initial Release |
# Chat History Design Guidelines
Source: https://docs.discord.com/developers/discord-social-sdk/design-guidelines/chat-history
Design guidelines for displaying and managing chat history from Discord.
## Style recommendations
Chat history is a highly requested **optional** feature that helps players pick up conversations where they left off—whether they're returning to a lobby or jumping back into a linked channel.
We recommend showing **timestamps** to give players better context about when messages were sent and seen.
## Display logic
If one player is messaging from a game lobby or linked channel, then chat history is preserved, even if the other player is messaging from Discord.
When players return to a lobby or open a linked channel, we recommend surfacing **10 to 15** of the most recent messages to provide meaningful context. This isn't a hard limit—older messages can be revealed through scroll or loading more content, depending on your UI constraints.
***
## Resources
* [Development Guide: Creating and Managing Lobbies](/developers/discord-social-sdk/development-guides/managing-lobbies)
## Change Log
| Date | Changes |
| ------------- | --------------- |
| July 02, 2025 | initial release |
# Connection Points
Source: https://docs.discord.com/developers/discord-social-sdk/design-guidelines/connection-points
Design guidelines for creating effective connection points between your application and Discord.
This guide provides best practices and design guidelines for integrating key connection points within your game. Connection points are crucial touchpoints where players can login to Discord, enhancing their overall gaming experience.
## Signing in
If the game has account management, then this connection point is **required**, otherwise it is not.
Discord's sign-in button is presented as the **primary option** to log-in for the player amongst the list of **external identity-providers** due to providing deeper user-benefits than a standard OAuth login.
Please use the [**Blurple Button**](/developers/discord-social-sdk/design-guidelines/branding-guidelines#buttons) button styling for the sign-in connection point.
## Friends list
If the player has not connected their Discord account yet, they will see a **persistent call-to-action** in their friend's list until they connect.
Select your preferred *button-styling* for the friends list connection point (see Brand guidelines section).
After the player connects their account, the connection point is no longer visible.
## Content Guidelines
Please use the strings shown here within the relevant contexts.
These connection point strings should be consistent across all games that use the Discord Social SDK to help the user build recognition, trust, and understanding when taking this action.
***
## Change Log
| Date | Changes |
| -------------- | --------------- |
| March 17, 2025 | initial release |
# Console Design Guidelines
Source: https://docs.discord.com/developers/discord-social-sdk/design-guidelines/consoles
Design guidelines for implementing Discord Social SDK features on gaming consoles.
To use the Discord Social SDK in your console games, you will need to request middleware approval and be an approved developer for the target console. Check out [this article](https://support-dev.discord.com/hc/en-us/articles/30209074764183) to learn more.
Exception: Prohibited Off-Platform Interactions by Sony
For integrations experienced on Sony Platforms (e.g. PlayStation 5)…
1. **Do not allow access to Account Linking** entry points.
2. **Do not allow access to Linked Channels**.
3. **Limit messaging and Invites to other players currently playing** the game.
4. **Do not display off-platform presence and logos** (including Discord).
Players expect a consistent Discord social experience across devices—whether on PC, mobile, or console. While the console experience largely mirrors other devices, the following slides highlight adjustments tailored to console navigation.
Console players **will need a secondary device** to experience the full benefits of the Discord Social SDK.
## Connect to Discord on console
The overall connection flow remains the same on console as other devices. After clicking an in-game CTA to connect to Discord, a screen with sign in options appears.
Users will need to use a mobile device or computer in order to avoid the clunky experience of signing in on a web browser using a controller.
A player without an existing Discord account can create one easily on the web.
Users can skip the device code screen by scanning the QR code with their mobile camera. They will be prompted to authorize the game. If the Discord app is not detected upon scanning, users are prompted to sign in and connect via the web browser.
If a player does not wish to use the QR scan, they will need to go to discord.com/activate to enter the 8-digit code.
## Chatting on console
Game chat will be available for games who support the feature on consoles. However, unsupported rich media from Discord (i.e. attachments, polls, voice messages, etc.) should not include a clickable link icon. Similar to the auth flow, we do not want to encourage players to visit Discord in the console web browser—they should use secondary devices to view this content.
## Friends list on console
Please leverage the same [friends list guidelines](/developers/discord-social-sdk/design-guidelines/unified-friends-list) previously documented in the playbook. There are no notable differences in the friends list on console in comparison to other devices. However, consoles do not support hover interactions so there needs to be console-friendly way for players to access secondary information (such as alternate identities) about friends in the list.
***
## Resources
* [Development Guide: Account Linking on Consoles](/developers/discord-social-sdk/development-guides/account-linking-on-consoles)
## Change Log
| Date | Changes |
| -------------- | --------------- |
| March 17, 2025 | initial release |
# Direct Messages Design Guidelines
Source: https://docs.discord.com/developers/discord-social-sdk/design-guidelines/direct-messages
Design guidelines for implementing Discord direct messaging features in your application.
## Badge when online elsewhere
The badging logic in direct-messages is consistent with the logic detailed in the [Unified Friends List](/developers/discord-social-sdk/design-guidelines/unified-friends-list) section.
If the user is **online elsewhere** and is not in the game client, their usernames will include a **Discord badge**. Once they're online in the game-client, they will not retain the Discord badge.
## Message styling
Style the Discord logo to match a player's username color. The logo should feel tied to a user's identity, not the message content.
## Unsupported rich media
Regardless of whether a user has signed into Discord, unsupported rich media content should be paired with an **external link icon**.
When content isn't clickable, don't show the icon.
***
## Resources
* [Development Guide: Direct Messages](/developers/discord-social-sdk/development-guides/sending-direct-messages)
## Change Log
| Date | Changes |
| -------------- | --------------- |
| March 17, 2025 | initial release |
# Game Friends Design Guidelines
Source: https://docs.discord.com/developers/discord-social-sdk/design-guidelines/game-friends
Design guidelines for displaying and managing game friends and Discord relationships.
Any in-game imagery used in is purely fictional for concept purposes.
## User needs
We've heard from players that they want more of a distinction between connections they make in-game, and friends they spend time with on Discord outside of and across games. Someone I party up with may not necessarily be my "friend".
Users want more controls over how in-game relationships extend beyond the game client with communication, presence, and identity.
## Game Friends — A new tier
Discord is introducing a new tier of friendship called **Game Friends**.
Game Friends are a way to create an in-game relationship with another player that provides all of the in-game benefits, but limits how the relationship extends outside the game.
## Game vs Discord Friends
### Communication
A Game Friend **can reach Discord-connected friends outside the game** by DMing those in the game who are offline — these messages will be received on the Discord client. Discord provides players a setting to disable receiving DMs from Game Friends.
### Presence
A Game Friend **cannot see your rich presence** beyond the game your friendship originated in. The exception is if both friends have their Discord accounts connected and are in a shared server.
## Equally-weighted
When adding a new friend, **present two options to players with "Game Friends" listed first**, since it's the more localized/limited friend-tier. Please ensure that neither type of friend is pre-selected in the UI — present them equally to the user (ordering aside for above reasons).
## Adding friends
If the user is not yet a friend, show both options of adding as either a Game or Discord Friend.
If a user is already your Game Friend, show the action to also add them on Discord.
## Removing friends
When removing a Game Friend or Discord Friend, show players the standard "Remove Friend" action. The SDK will remove the friend across whatever friendships are applicable.
## Style guidelines
When removing a Game Friend or Discord Friend, show players the standard "Remove Friend" action. The SDK will remove the friend across whatever friendships are applicable.
## UX, not UI
The visual styling of the "Add Friend" menu-items is not intended to be prescriptive. So long as both options are equally-weighted, please use whichever UI fits your game's needs.
## Setting on Discord
This mock showcases how the corresponding DM looks within Discord's UI.
***
## Resources
* [Development Guide: Managing Relationships](/developers/discord-social-sdk/development-guides/managing-relationships)
## Change Log
| Date | Changes |
| -------------- | --------------- |
| March 17, 2025 | initial release |
# Linked Channels Design Guidelines
Source: https://docs.discord.com/developers/discord-social-sdk/design-guidelines/linked-channels
Design guidelines for integrating Discord channel functionality into your application.
## Link in-game chats to Discord
Empower players to continue their conversations beyond the game. Now you can link an in-game chat to a text-channel on Discord.
## Style guidelines
#### Feature Name & Language
The official feature-name that will be reflected across the Discord client is "Linked Channels." Please use the following language when referring to this feature to promote player comprehension across clients.
* "Linked to \{Discord/GameTitle}"
* "Link a \{groupChat} to Discord"
* "Edit/Remove Channel Link"
#### Entrypoints
Please use the string guidance from above: "Link \{groupChat} to Discord." For interface, use whichever UI treatment that fits your game's needs.
## Selecting a channel
Players require a channel-selection flow where they can pick a text-channel to link their game-chat with. Only one text-channel can be linked at a time. Furthermore, the player must have the permissions to manage the channel.
Please include the following in the selection UI:
#### UI Elements
* Description of what a channel link does and **who can read and write to the channel** after establishing the link
* List of **servers** and their nested **text-channels** with respective iconography (ie, private channel has a lock)
* **Search bar** for channel-names
## Setup warning
Players can link channels with **limited access** on Discord. Doing so will allow players from their game-chat to also read and send messages to this channel, **regardless of whether or not they have permissions on Discord**. In short, linking a game-channel will bypass that channel's existing permissions on Discord.
Show a **warning** when a user is about to link to a channel with limited access. We strongly recommend you use the copy shown to the right image.
## Success state & Entrypoint
Once the channel link is established, show a **success state** to the user detailing which text-channel they've linked to on Discord.
Separately, create a **persistent entrypoint** somewhere in the game UI. This persistent entrypoint should also show details such as the Discord server and channel name. It will also serve as the entrypoint for managing the linked channel (next slide).
[You can download the official "Linked Channels" channel-icon in our GitHub repository.](https://github.com/discord/discord-api-docs/blob/main/resources/discord-social-sdk)
## Removing Channel Link
Players can navigate to the relevant channel settings on Discord from the "**Edit settings**" menu item. Please pair with an "open in new window" icon.
Players can remove the channel link from either the game client or the Discord client.
When "Remove channel-link" is interacted with, show the player a **confirmation** dialog to confirm this action.
***
## Resources
* [Development Guide: Linked Channels](/developers/discord-social-sdk/development-guides/linked-channels)
* [Design Assets: Linked Channel Icon](https://github.com/discord/discord-api-docs/blob/main/resources/discord-social-sdk)
## Change Log
| Date | Changes |
| -------------- | --------------- |
| March 17, 2025 | initial release |
# Principles
Source: https://docs.discord.com/developers/discord-social-sdk/design-guidelines/principles
Core design principles for creating effective Discord Social SDK integrations.
Any in-game imagery used in is purely fictional for concept purposes.
## 1. Add Value for the Player
This integration aims to bring value to the player's gaming experience. Branded connection points should highlight said value-add based on the context.
## 2. Foster trust with Discord's brand
Lean into Discord's visual-styling to establish trust through brand recognition. The more players that connect, the more robust the social graph is for the game.
## 3. Promote Understanding through consistency
Use consistent branding and user-flows across games to build expected behavior. We care about the UX, not the UI.
## 4. Prioritize the player's immersion
Our focus is on preserving the player's immersion in the game. There are no requirements to replicate Discord's UI. The visual-design should feel as seamlessly integrated into the game as our SDKs.
## 5. Uphold data privacy, transparency, and control
Each platform is in control and responsible for the data on their respective platforms. Players are informed of where their data shows up.
***
## Change Log
| Date | Changes |
| -------------- | --------------- |
| March 17, 2025 | initial release |
# Provisional Accounts Design Guidelines
Source: https://docs.discord.com/developers/discord-social-sdk/design-guidelines/provisional-accounts
Design guidelines for handling provisional Discord accounts and onboarding users.
## For those who don't connect a Discord account
Not all users are going to link their Discord account to their game. This creates the potential burden of having to maintain **two different friend systems** for each account type.
The Discord Social SDK has built provisional accounts as a way for game-devs to create a lightweight, **limited Discord account** for unlinked users, so you can use the same APIs regardless of whether a user has connected their Discord account or not.
## What is a provisional account?
To players, a provisional account is simply a **standard account** that has yet to be linked to Discord. They have access to standard communication features you'd expect any game user to have.
The complexity around provisional accounts that game-developers have to handle are invisible to players in the game.
## How provisional accounts look within the game
Provisional accounts **won't have the Discord badge** by their username in the friends list and other contexts where their username may appear because they have not connected their Discord account.
Only players who've connected their Discord accounts and have provisional account friends from other games using the Discord Social SDK will see this state.
## How provisional accounts look within Discord
Provisional accounts will leverage **branded avatars** and **text-hints** on Discord to differentiate them from standard, Discord users.
Because provisional accounts have limited capabilities, for example, you cannot start a voice or video call with them, it's important to visually distinguish them from Discord users.
***
## Resources
* [Development Guide: Using Provisional Accounts](/developers/discord-social-sdk/development-guides/using-provisional-accounts)
## Change Log
| Date | Changes |
| -------------- | --------------- |
| March 17, 2025 | initial release |
# Designing for Account Linking
Source: https://docs.discord.com/developers/discord-social-sdk/design-guidelines/signing-in
Design guidelines for implementing Discord sign-in flows and authentication.
Any in-game imagery used in is purely fictional for concept purposes.
## Overall flow
Sign-in or log-in flows all begin with the interaction of a [**Discord-styled button**](/developers/discord-social-sdk/design-guidelines/branding-guidelines#buttons).
The Discord Social SDK will support the user-journeys for those who already **have a Discord account** and those who **do not**. We will similarly support those who have Discord installed as well as those who do not (via browser).
A player can also proceed **without** connecting their Discord and continue onwards to playing their game.
### Connecting your Discord account via Browser (gif)
## Overlay Authorization
Discord's [Overlay](https://support.discord.com/hc/en-us/articles/217659737-Game-Overlay-101) feature can be used to enable authorization without ever leaving the game. This is available for games who support the feature.
## Before the user connects
We recommend adding a **one-time user education** modal based on feedback we've heard from developers and players.
This modal should include some of the key value props that the Discord Social SDK provides. The modal should be placed contextually **in the game**.
#### Suggested Moments
* Trigger upon game-start for first time after the Discord Social SDK integration is launched to players
* Show when player interacts with the friends list for the first time
* Show in the authorization flow once a player taps the Discord connection point
## After the user connects
We recommend adding a **success state** in the game when the authorization completes — styled in a way that fits the game's aesthetic.
#### Content
Include the following points sequentially in the game' s writing-style. Remove any points that covers features that the game does not utilize.
* You successfully connected your Discord account
* You can chat with your friends here and on Discord
* It might take some time for your game friends to show up in your Discord
## Error State
The user will also need to be shown an **error state** if the authorization fails — also styled in a way that fits the game's aesthetic.
Please include these key points sequentially in the game's writing-style:
* We were unable to connect your Discord account
* A call-to-action that redirects back to Discord with the initial authorization modal
***
## Resources
* [Development Guide: Account Linking with Discord](/developers/discord-social-sdk/development-guides/account-linking-with-discord)
## Change Log
| Date | Changes |
| -------------- | --------------- |
| March 17, 2025 | initial release |
# Social Settings Design Guidelines
Source: https://docs.discord.com/developers/discord-social-sdk/design-guidelines/social-settings
Design guidelines for implementing social settings and privacy controls.
## Evolving with users
Discord is listening to feedback and releasing settings for players to tailor their social experience to their needs. These settings will continue to change and evolve with the SDK and Discord client.
Discord Social SDK settings are global, meaning they will impact all games that use the Discord Social SDK. These settings live in the Discord client.
## Main entrypoint
An entrypoint is required in games to
[send players to the relevant Discord client settings page](/developers/discord-social-sdk/development-guides/sending-direct-messages#ingame-direct-message-settings).
Only players who have their Discord account connected should see this.
Exactly like the auth flow, players will be redirected to the Discord client to manage their social settings. If they do not have the client, the browser will be the fallback.
## Content guidelines
The official settings title for players is "Discord Social Experience." Use this language when referring to Social SDK settings to players.
The description is optional, but if your game settings has the space, we recommend describing what users should expect to find when they navigate to Discord.
***
## Resources
* [Development Guide: Direct Messages](/developers/discord-social-sdk/development-guides/sending-direct-messages)
## Change Log
| Date | Changes |
| ------------ | --------------- |
| May 08, 2025 | Initial Release |
# User Status & Rich Presence Design Guidelines
Source: https://docs.discord.com/developers/discord-social-sdk/design-guidelines/status-rich-presence
Design guidelines for displaying user status and Rich Presence information.
This guide provides best practices and design guidelines for integrating status and rich presence features within your game. These features are essential for enhancing player engagement and providing real-time information about player activities.
By following this guide, you will learn how to:
* Implement a status matrix to display player statuses in your friends list.
* Apply core style guidelines to ensure consistency and player comprehension.
* Customize the appearance to fit your game while maintaining the essential elements of status indicators.
## Core statuses, plus game-specific
## Status matrix
## Side-by-side
Any of the **core statuses** (online, idle, DND, offline) will always be the same between the game and the Discord client.
You can customize the status icon, but custom icons will only render within the game.
## Core style guidelines
To ensure player comprehension, keep the **core colors, symbols, and information architecture consistent** across Discord-powered games — with choice of styling of applied.
In other words, as long as the status colors read as yellow/green/red, and the symbols as moon/circle/minus/offline, **all other styling is the game's choice**.
Avatars are not required.
## Rich Presence
The game developer should set rich presence — this will show in both the Discord and game client.
Please refer to [**Setting Rich Presence**](/developers/discord-social-sdk/development-guides/setting-rich-presence) for more information on Rich Presence, such as other types of traits that can be included.
***
## Resources
* [Development Guide: Setting Rich Presence](/developers/discord-social-sdk/development-guides/setting-rich-presence)
## Change Log
| Date | Changes |
| -------------- | --------------- |
| March 17, 2025 | Initial Release |
# Unified Friends List Design Guidelines
Source: https://docs.discord.com/developers/discord-social-sdk/design-guidelines/unified-friends-list
Design guidelines for creating unified friends lists that combine Discord relationships.
This guide provides best practices and design guidelines for integrating a unified friends list within your game. The unified friends list is a crucial feature that allows players to see and interact with their friends across different platforms and games, enhancing their overall social gaming experience.
By following this guide, you will learn how to:
* Implement sectioning to organize friends by availability and game status.
* Display the Discord badge to indicate universal communication capabilities.
* Prioritize and display different identities based on the player's relationship with the game and Discord.
* Ensure consistency and familiarity in naming conventions to reduce confusion and enhance user experience.
These design guidelines are intended to help you create a seamless and intuitive friends list that leverages Discord's social features to their fullest potential. Let's get started!
***
## Figma File - The Social SDK: Friend List Starter Pack
We’ve created a Figma resource with a templated design-system of building out your game's friends list. Change
various components to match your game’s visual-language, and see it update in realtime. We’ve also tossed in a
few common use-cases, as well as an example of one of our partnered games, SUPERVIVE.
We'd love your feedback! Join [Discord Developers Server](https://discord.gg/discord-developers) and create a post in
the `#social-sdk-dev-help` channel if you would like to see more resources like this, or if you have suggestions
for improvement.
[
](https://www.figma.com/community/file/1512487996808869592/the-social-sdk-friend-list-starter-pack)
[See Figma Resource↗](https://www.figma.com/community/file/1512487996808869592/the-social-sdk-friend-list-starter-pack)
***
## Sectioning
Sections are ordered by descending availability to help users find friends they can play with.
The **Online — GameTitle** section shows friends who are online in the same game and are compatible to play with.
The **Online — Elsewhere** section shows friends who are online but not in the same game. However, they can be messaged or invited to play the game.
## Discord = Communication
Show the Discord badge next to a player's name when they are **online elsewhere** and have **connected their Discord** account. Provisional accounts will not have a badge.
The Discord logo represents **universal communication**. It aims to convey to users: “As a player, I know I can message or invite this friend to play a game anywhere Discord is present.” This means via phone, computer, and even console.
## Identities
If the friend **owns the game**, display their in-game identity (username). Prioritize what users are already familiar with.
If the friend does not own the game, use their **Discord Display Name**.
If the friend does not own the game nor have a linked Discord account, fall back to their **Provisional identity**.
## Examples of friends lists
A player's **console ID** is effectively their **game ID**, as consoles require this. As such, it's likely the set of names players will be most familiar with.
The examples to the right show how the hierarchy translates to consoles.
## How did we land here?
It reduces sudden **name changes** within the game, which can be confusing to the player, and reduce erratic UI-width shifts.
It's also **scalable** and **inclusive** of provisional accounts who do not have Discord accounts connected.
## Discord Display Names
When referring to a player's Discord Identity, please use their **Discord Display Name**, and not their username.
See [How to Manage Special Characters in Discord Display Names](/developers/discord-social-sdk/how-to/handle-special-characters-display-names) for guidance on handling special characters in text chat and friend lists.
## Scaling to Multiple Platforms
If your game uses more than one platform's friend graph, combine them into "Online Elsewhere."
Prioritize player needs by elevating available players to the top of the friends list, regardless of platform.
Avoid friend-lists organized by company. This hierarchy dilutes the value-prop of putting online players first, in favor of platform delineation.
***
## Resources
* [Development Guide: Creating a Unified Friends List](/developers/discord-social-sdk/development-guides/creating-a-unified-friends-list)
* [Figma File - The Social SDK: Friend List Starter Pack ↗](https://www.figma.com/community/file/1512487996808869592/the-social-sdk-friend-list-starter-pack)
## Change Log
| Date | Changes |
| -------------- | ---------------------------- |
| June 25, 2025 | Added Figma UFL starter pack |
| March 17, 2025 | initial release |
# Account Linking from Discord
Source: https://docs.discord.com/developers/discord-social-sdk/development-guides/account-linking-from-discord
Adopt the new account linking APIs to allow Discord to show your players entry points for account linking within the Discord client
## Overview
New to account linking? Read the [Account Linking overview](/developers/platform/account-linking) to understand what it is, how it works, and which flow to use.
To give players more flexibility and options for account linking, Discord can show prompts and buttons throughout the Discord client that encourage users to link their accounts with your game. These are called entry points for account linking.
Higher account linking rates lead to better social features, improved engagement, and a stronger connection between your game and Discord users.
This guide shows you how to enable Discord to display these account linking prompts to your players.
### Prerequisites
Before enabling these entry points, you must have:
* **Discord application setup**
* Created a Discord application in the [Developer Portal](https://discord.com/developers/applications)
* Configured [OAuth2 settings](https://discord.com/developers/applications/select/oauth2) with appropriate redirect URLs
* **Implemented account linking in your game**
* Discord Social SDK downloaded and integrated into your game
* SDK initialization working (can connect to Discord)
* [Basic account linking flow](/developers/discord-social-sdk/development-guides/account-linking-with-discord) already implemented in your game
* **Development environment ready**
* Game can successfully authenticate users with Discord OAuth2
If you haven't completed these prerequisites, we recommend first following the [Getting Started](/developers/discord-social-sdk/getting-started) guide.
***
## Entry Points & Account Linking Flows
### What are Entry Points?
Entry points are buttons, prompts, and UI elements that Discord shows throughout the Discord client to encourage account linking. Think of them as "Link your account" buttons that appear in various places where users might see your game.
### Example of an Entry Point
Discord automatically chooses which entry points to show based on what features you've implemented and whether your game is currently running.
Some of the current Discord entry points include:
**Game Detection**
Discord detects the player is playing your game and provides an entry point to link if they haven't already.
**Game Invites**
Players are optionally able to link when receiving an invite for your game, which helps them get set up and playing with their friends faster.
**Claiming Game Shop Items**
Players need to account link when redeeming items from your Game Shop to be able to receive their in-game rewards.
### Account Linking Flows
At this time, Discord provides two different flows for account linking from the client, the connected game flow and the web flow. When a user clicks on one of the entry points within their Discord client, it will choose one of the currently available flows to send the user through. To provide a good user experience, flows are chosen in the following priority order:
* [Connected game flow](/developers/discord-social-sdk/development-guides/account-linking-from-discord#connected-game-flow)
* [Web flow](/developers/discord-social-sdk/development-guides/account-linking-from-discord#web-flow)
Choose which flows work best for your game and follow the implementation guides below. You can implement one or both flows to give your players more options for linking their accounts.
***
## Connected Game Flow
This is our preferred account linking flow as we think it provides the best user experience when it's available. The connected game flow will send the user into your game client to begin the account linking flow.
### Prerequisites
* Upgrade to SDK version 1.6 or higher
* Implement [account linking](/developers/discord-social-sdk/development-guides/account-linking-with-discord) in your game
### Implementing the Connected Game Flow
Once you have adopted SDK version 1.6 or higher, there will be two new methods on the Client object, [`Client::RegisterAuthorizeRequestCallback`] and [`Client::RemoveAuthorizeRequestCallback`]. These two new methods will allow you to enable and disable this flow while your game is running.
Before calling [`Client::RegisterAuthorizeRequestCallback`], ensure
[`Client::SetApplicationId`] has been called. This ensures that your game is
properly identifying itself to the Discord client and that all requests are
associated with the correct application.
Calling [`Client::RegisterAuthorizeRequestCallback`] with a callback function will signal to the Discord client that your game is running and able to start the account linking process. You should run this as soon as your game is able to handle an account link, for instance just after your user has logged in or reached the main menu.
When a user clicks one of the account linking entry points in their Discord client, the SDK will run your callback. Treat this as if the user had just clicked one of your in-game account linking buttons and begin the authorization flow right away.
When your game receives this callback, the user is most likely focused on the
Discord client, not the game client. Make sure to pull focus or otherwise
alert the user that something is happening in the game. The Discord client
will notify them to check the game as well. For a more seamless
experience, you should pull them back into the game.
Calling [`Client::RemoveAuthorizeRequestCallback`] will signal to the Discord client that your game is no longer able to start the account linking process. Call this whenever your game client is entering a state where starting the account linking process wouldn't work, for instance if the player enters a match, logs out, or is in some other full screen flow like a cutscene.
This is all you need to enable the connected game flow, you can test and debug this flow using our new [built-in developer tools](/developers/discord-social-sdk/how-to/debug-log#client-tools) in the Discord client.
```cpp theme={"system"}
// If you haven't already
client.SetApplicationId("123456789");
client.RegisterAuthorizeRequestCallback([client, myGameAuthorizationHandler]() {
// Pull focus into your game using native APIs
// ...
// Run your normal authorization flow
myGameAuthorizationHandler.StartDiscordAccountLink();
});
```
### Connected Game Best Practices
When a user clicks the account link button in their Discord client, this is a high intent action. We recommend skipping any extra upsells you have until after the user has linked their account, as any extra steps added to this linking flow increases the areas where users may drop off. Show them any upsells after they've linked to reaffirm all the new features and any rewards they may have unlocked by linking their account.
Ensure you're enabling and disabling the callback at the appropriate times. If a user clicks an entry point in their Discord client and it launches the account linking flow in the middle of a match, this can interrupt gameplay and pull them out of your intended experience.
Don't worry about checking if the user is already linked, the Discord client will handle hiding the entry point for users that have already linked their accounts. This will keep your implementation simpler and reduce surface for issues.
## Web Flow
The web flow is an alternative to the connected game flow, allowing you to send users to a webpage to begin the account linking process.
At this time, this flow is only available to select partners.
### Prerequisites
* Implement the [standard OAuth2 flow](/developers/topics/oauth2) on your website
* Set your **Connection Entrypoint URL** in the [Developer Portal](https://discord.com/developers/applications) to the URL that starts your OAuth2 web flow
### Implementing the Web Flow
Ensure your webpage meets the following requirements:
* If the user isn't signed in to their game account, it should prompt them to sign in and then take them immediately back to the authorization flow.
* The authorization should request the same scopes that you request in your game
* After authorization is complete, the token should be saved to the user's account the same way you would during in-game authorization so that it's ready the next time the user launches their game
* If the user is currently in game, send an update to their client with the token and call [`Client::UpdateToken`] to sign them in right away without needing to restart their game
***
## Next Steps
Now that you've successfully implemented account linking with Discord, you can integrate more social features into your game.
}>
Design guidelines for account linking and user authentication
}>
Combine Discord and game friends into a single list for easy management.
}>
Display game status and information to Discord friends.
Need help? Join the [Discord Developers Server](https://discord.gg/discord-developers) and share questions in the `#social-sdk-dev-help` channel for support from the community.
If you encounter a bug while working with the Social SDK, please report it here: [https://dis.gd/social-sdk-bug-report](https://dis.gd/social-sdk-bug-report)
***
## Change Log
| Date | Changes |
| ----------------- | --------------- |
| December 16, 2025 | initial release |
[`Client::RegisterAuthorizeRequestCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a5f34b873e127a446c9ab549e4588ccd7
[`Client::RemoveAuthorizeRequestCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ab7e48864b0cedf3e8572a228ca401f2a
[`Client::SetApplicationId`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ad452335c06b28be0406dab824acccc49
[`Client::UpdateToken`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a606b32cef7796f7fb91c2497bc31afc4
# Account Linking on Consoles
Source: https://docs.discord.com/developers/discord-social-sdk/development-guides/account-linking-on-consoles
Implement Discord account linking on gaming consoles.
## Overview
New to account linking? Read the [Account Linking overview](/developers/platform/account-linking) to understand what it is, how it works, and which flow to use.
This guide will walk you through implementing OAuth2 device authorization for console users, allowing them to log in to your game using their Discord account.
By following this guide, you will learn how to:
* Initiate OAuth2 device authorization
* Handle user authentication via QR codes or manual entry
* Exchange the device code for an access token
* Implement automatic token handling with the SDK
### Prerequisites
Before you begin, make sure you have:
* Read the [Core Concepts](/developers/discord-social-sdk/core-concepts) guide to understand:
* OAuth2 authentication flow
* Discord application setup
* SDK initialization
* Set up your development environment with:
* Discord application created in the [Developer Portal](https://discord.com/developers/applications)
* Discord Social SDK downloaded and configured
* Basic SDK integration working (initialization and connection)
To use the Discord Social SDK in your console games, you will need to request middleware approval and be an approved developer for the target console. Check out [this article](https://support-dev.discord.com/hc/en-us/articles/30209074764183) to learn more.
If you haven't completed these prerequisites, we recommend following the [Getting Started](/developers/discord-social-sdk/getting-started) guide.
***
### OAuth2 Device Authorization Flow
Console users cannot authenticate via a web browser. Instead, they follow these steps:
1. The game requests a device code from Discord
2. To approve the authorization request, The user scans a QR code or enters a user code at `discord.com/activate`.
3. Your game polls the Discord API to exchange the device code for an access token.
The SDK can manage this process automatically or allow manual token handling.
The OAuth2 flow requires a user's account to be verified
***
## Automatic Token Handling
This method requires enabling **Public Client** for your app. Most games will not want to ship with this enabled. [Learn more](/developers/discord-social-sdk/core-concepts/oauth2-scopes#oauth2-client-types)
The SDK automates all of the OAuth2 device authorization flow steps with a single call to [`Client::GetTokenFromDevice`]. This method will handle the entire authorization process, including polling for the access token.
Once you have the access token, you can authenticate the user with [`Client::UpdateToken`] and call [`Client::Connect`] to establish a connection with Discord.
```cpp theme={"system"}
discordpp::DeviceAuthorizationArgs args{};
args.SetClientId(APPLICATION_ID);
args.SetScopes(discordpp::Client::GetDefaultPresenceScopes());
client->GetTokenFromDevice(args, [client](
discordpp::ClientResult result,
std::string accessToken,
std::string refreshToken,
discordpp::AuthorizationTokenType tokenType,
int32_t expiresIn,
std::string scope) {
if (result.Successful()) {
std::cout << "🔓 Access token received! Establishing connection...\n";
client->UpdateToken(discordpp::AuthorizationTokenType::Bearer, accessToken, [client](discordpp::ClientResult result) {
client->Connect();
});
} else {
std::cerr << "❌ Failed to get token from device \n";
}
});
```
***
## Manual Flow for Console Authorization
If you plan to handle console authorization manually, you can follow these steps to authorize a user.
We'll be following the same OAuth2 device authorization flow as the automatic method, but you'll need to manually handle the polling and token exchange.
1. [Request a device code from Discord](/developers/discord-social-sdk/development-guides/account-linking-on-consoles#step-1-request-a-device-code-from-discord)
2. [Display the user verification information](/developers/discord-social-sdk/development-guides/account-linking-on-consoles#step-2-display-authorize-screen-with-qr-code-and-user-code) or open the authorization screen (optional)
3. [Poll for the user's authorization](/developers/discord-social-sdk/development-guides/account-linking-on-consoles#step-3-poll-for-users-authorization)
4. [Exchange the device code for an access token](/developers/discord-social-sdk/development-guides/account-linking-on-consoles#step-4-exchange-device-code-for-access-token)
5. [Handle the token response](/developers/discord-social-sdk/development-guides/account-linking-on-consoles#step-5-handle-token-response) and close authorization screen (optional)
### Step 1: Request a Device Code from Discord
Your game must request a device and user code from the Discord API.
```python theme={"system"}
import requests
API_ENDPOINT = "https://discord.com/api/v10"
CLIENT_ID = "YOUR_CLIENT_ID"
CLIENT_SECRET = "YOUR_CLIENT_SECRET"
SCOPE = "sdk.social_layer"
def authorize_device():
data = {"scope": SCOPE}
headers = {"Content-Type": "application/x-www-form-urlencoded"}
response = requests.post(f"{API_ENDPOINT}/oauth2/device/authorize", data=data, headers=headers, auth=(CLIENT_ID, CLIENT_SECRET))
response.raise_for_status()
return response.json()
```
#### Example Response
```json theme={"system"}
{
"device_code": "",
"user_code": "",
"verification_uri": "https://discord.com/activate",
"verification_uri_complete": "https://discord.com/activate?user_code=",
"expires_in": 300,
"interval": 5
}
```
### Step 2: Display Authorize Screen with QR Code and User Code
To open an authorization screen after requesting the user code, use [`Client::OpenAuthorizeDeviceScreen`].
```cpp theme={"system"}
client->OpenAuthorizeDeviceScreen(user_code);
```
You can also display the `verification_uri_complete` or `verification_uri` with `user_code` in your game's interface to allow the user to enter the code manually.
Once the user approves the authorization request from a web browser or their mobile device, the `device_code` from Step 1 is ready to be exchanged for an access token.
### Step 3: Poll for User's Authorization
While the user completes the authorization request, your game must poll the Discord Oauth2 token endpoint to exchange the device code for a valid access token.
You should poll this endpoint using the provided `interval` until the code expires after `expires_in` or succeeds. These values are available from the Authorize response above. If the code expires, you'll want to start over with a new authorization request or cancel the authorization.
### Step 4: Exchange Device Code for Access Token
Once the user completes authentication on Discord, your game must exchange the device code for an access token.
```python theme={"system"}
def exchange_device_code(device_code):
data = {
"grant_type": "urn:ietf:params:oauth:grant-type:device_code",
"device_code": device_code
}
headers = {"Content-Type": "application/x-www-form-urlencoded"}
response = requests.post(f"{API_ENDPOINT}/oauth2/token", data=data, headers=headers, auth=(CLIENT_ID, CLIENT_SECRET))
response.raise_for_status()
return response.json()
```
### Step 5: Handle Token Response
Once exchanged, the API returns an access token to be used with [`Client::UpdateToken`] and [`Client::Connect`] to start using Discord Social SDK features.
#### Example Response
```json theme={"system"}
{
"access_token": "",
"token_type": "Bearer",
"expires_in": 604800,
"refresh_token": "",
"scope": "sdk.social_layer"
}
```
If you followed the manual flow, you'll need to call [`Client::CloseAuthorizeDeviceScreen`] to close the authorization screen.
### Example for Manual Token Authorization
Here is an example of implementing the manual authorization flow in Python.
```python theme={"system"}
import requests
import json
import time
API_ENDPOINT = 'https://discord.com/api/v10'
CLIENT_ID = 'YOUR_CLIENT_ID'
CLIENT_SECRET = 'YOUR_CLIENT_SECRET'
SCOPE = 'sdk.social_layer'
def authorize_device():
data = {
'scope': SCOPE
}
headers = {
'Content-Type': 'application/x-www-form-urlencoded'
}
r = requests.post('%s/oauth2/device/authorize' % API_ENDPOINT, data=data, headers=headers, auth=(CLIENT_ID, CLIENT_SECRET))
r.raise_for_status()
return r.json()
def exchange_device_code(device_code):
print("Attempting to exchange device code for access token...")
data = {
"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, auth=(CLIENT_ID, CLIENT_SECRET))
r.raise_for_status()
response = r.json()
return r.json()
def poll_token_exchange(device_code, interval, expires_in):
start_time = time.time()
while time.time() - start_time < expires_in:
try:
print("Checking for authorization...")
result = exchange_device_code(device_code)
return result
except requests.exceptions.HTTPError as e:
if e.response.status_code == 400:
# Authorization is pending, wait and try again
print(f"Waiting {interval} seconds for user to authorize...")
time.sleep(interval)
else:
# Some other error occurred
raise e
raise Exception(f"Authorization timed out after {expires_in} seconds")
# Step 1: Get device authorization details
device_auth = authorize_device()
# Step 2: Display authorization details to user
print("\nPlease visit:", device_auth["verification_uri_complete"])
# Step 3: Poll for token exchange
try:
access_token = poll_token_exchange(
device_auth["device_code"],
device_auth["interval"],
device_auth["expires_in"]
)
print("\nSuccess! Access Token:", access_token["access_token"])
except Exception as e:
print("\nError:", str(e))
# Step 4: Use the access token with discordpp::Client::UpdateToken and discordpp::Client::Connect
```
***
## Working with Tokens
Once you receive the player's access and refresh tokens, you can set the access token in the SDK with [`Client::UpdateToken`]. At this point, you're authorized and ready to use [`Client::Connect`].
```cpp theme={"system"}
client->UpdateToken(discordpp::AuthorizationTokenType::Bearer, accessToken, [client](discordpp::ClientResult result) {
client->Connect();
});
```
You'll want to store the access and refresh tokens for the player to use in future sessions.
Please note that `access_token` values do expire. You'll need to make use of the `refresh_token` to refresh the player's token, which is covered under [Refreshing Access Tokens](/developers/discord-social-sdk/development-guides/account-linking-with-discord#refreshing-access-tokens).
***
## Next Steps
Now that you've successfully implemented account linking with Discord on a console, you can integrate more social features into your game.
}>
Combine Discord and game friends into a single list for easy management.
}>
Display game status and information to Discord friends.
}>
Follow our design guidelines when integrating Discord features into a console game.
Need help? Join the [Discord Developers Server](https://discord.gg/discord-developers) and share questions in the `#social-sdk-dev-help` channel for support from the community.
If you encounter a bug while working with the Social SDK, please report it here: [https://dis.gd/social-sdk-bug-report](https://dis.gd/social-sdk-bug-report)
***
## Change Log
| Date | Changes |
| -------------- | --------------- |
| March 17, 2025 | Initial release |
[`Client::CloseAuthorizeDeviceScreen`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a4fcdd697702d086a8170a2d60a69acb8
[`Client::Connect`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a873a844c7c4c72e9e693419bb3e290aa
[`Client::GetTokenFromDevice`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a4a9c666b2d30bae0a16f5afd7ccee60d
[`Client::OpenAuthorizeDeviceScreen`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#af29a13acc992a75fc0870051ff68575b
[`Client::UpdateToken`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a606b32cef7796f7fb91c2497bc31afc4
# Account Linking on Mobile
Source: https://docs.discord.com/developers/discord-social-sdk/development-guides/account-linking-on-mobile
Implement OAuth2 authentication for iOS and Android with deep linking and PKCE.
## Overview
New to account linking? Read the [Account Linking overview](/developers/platform/account-linking) to understand what it is, how it works, and which flow to use.
This guide explains how to implement OAuth2 authentication for mobile users (iOS and Android), allowing them to link their Discord accounts with your game on mobile devices.
Mobile authentication uses **deep linking** to provide a seamless experience. When the Discord mobile app is installed, users are automatically redirected to Discord to authorize your game, then returned to your game via a custom URL scheme.
As of Discord Social SDK 1.5, mobile account linking has been significantly simplified through native deep-link authentication support.
### Key Differences from Desktop Authentication
Mobile platforms have unique requirements that differ from desktop:
* **Custom URL schemes** (deep links) are required instead of HTTP redirects
* **PKCE (Proof Key for Code Exchange)** is **mandatory** for all mobile apps using deep links, regardless of whether you're using a public or confidential client
* **Platform-specific configuration** is required (Info.plist for iOS, AndroidManifest.xml for Android)
### Prerequisites
Before you begin, make sure you have:
* Read the [Core Concepts](/developers/discord-social-sdk/core-concepts) guide to understand:
* OAuth2 authentication flow
* Discord application setup
* SDK initialization
* Set up your development environment with:
* Discord application created in the [Developer Portal](https://discord.com/developers/applications)
* Discord Social SDK version 1.5 or higher downloaded and configured
* [Basic account linking flow](/developers/discord-social-sdk/development-guides/account-linking-with-discord) working
on desktop
If you haven't completed these prerequisites, we recommend first following the [Getting Started](/developers/discord-social-sdk/getting-started) guide.
***
## Configure OAuth2 Redirect URI
For OAuth2 to work correctly on mobile, you must first register the correct redirect URI for your app in the [**Discord Developer Portal**](https://discord.com/developers/applications/select/oauth2).
| Platform | Redirect URI |
| -------------------------- | ---------------------------------------------------------------------------------------------------- |
| **Mobile (iOS & Android)** | `discord-YOUR_APP_ID:/authorize/callback` *(replace `YOUR_APP_ID` with your Discord application ID)* |
The redirect URI must use your application ID in the format `discord-YOUR_APP_ID:/authorize/callback`. Do not use `http://127.0.0.1/callback` on mobile.
***
## Unity Setup
Please follow the [Unity Getting Started guide](/developers/discord-social-sdk/getting-started/using-unity) for general setup instructions. There are additional mobile-specific configurations you'll need to complete:
### iOS Configuration
#### 1. Configure URL Scheme in Unity Project Settings
To enable [`Client::Authorize`] support, configure a callback URL scheme:
1. Open Project Settings: `Edit -> Project Settings...`
2. Navigate to the `Player` section
3. Select the `iOS` tab
4. Under `Other Settings`, locate `Supported URL Schemes`
5. Add `discord-YOUR_APP_ID` to the list (e.g., if your application ID is `123456`, add `discord-123456`)
#### 2. Set Microphone Usage Description
In the same Player settings page:
1. Set `Microphone Usage Description` to a valid description
2. This string will be displayed by iOS when microphone permissions are requested
3. Required for voice support
#### 3. Configure Info.plist for Deep Linking
For native authentication to work, you must update your `Info.plist` to include the `discord` scheme:
```
LSApplicationQueriesSchemes
discord
```
This allows your app to detect if the Discord mobile app is installed and deep-link into it for authentication.
#### 4. Enable Background Voice Support (Optional)
To enable voice support while your game is backgrounded:
1. Edit your `Info.plist` to enable the appropriate background modes
2. A build postprocessor is supplied in the [Unity sample project] that you may copy into your own project, located at
`Assets/Scripts/Editor/VoicePostBuildProcessor.cs`
For background voice, you should configure *Audio, AirPlay, and Picture in Picture* mode, **not** Voice over IP. See [Configuring your app for Media Playback](https://developer.apple.com/documentation/avfoundation/configuring-your-app-for-media-playback?language=objc) for details.
### Android Configuration
#### 1. Configure Custom URL Scheme
[`Client::Authorize`] requires an activity with a custom URL scheme to be added to your application manifest:
1. An example build processor is provided in the [Unity sample project] at `Assets/Scripts/Editor/AndroidPostBuildProcessor.cs`
2. Alternatively, manually add an intent filter to your `AndroidManifest.xml`:
```
```
Replace `YOUR_APP_ID` with your actual Discord application ID (e.g., `discord-1234567890123456789`).
#### 2. Add androidx.browser Dependency
Authorization requires `androidx.browser` as a Gradle dependency:
* If you use **Google External Dependency Manager** in your project, a suitable dependencies XML file is provided as part of the Unity plugin
* Otherwise, you'll need to add this dependency manually to your `build.gradle`:
```
dependencies {
implementation 'androidx.browser:browser:1.8.0'
// Your other dependencies...
}
```
#### 3. Android Permissions
The Android SDK uses the following permissions:
```
```
If your application does not use voice features, you may remove all permissions except `INTERNET` using the `tools:node="remove"` attribute [in your AndroidManifest.xml](https://developer.android.com/build/manage-manifests#node_markers).
Following the Getting Started guide for Unity? Continue with [Step 8: Connect the SDK to Discord](/developers/discord-social-sdk/getting-started/using-unity#step-8-connect-the-sdk-to-discord).
***
## Unreal Engine Setup
Please follow the [Unreal Engine Getting Started guide](/developers/discord-social-sdk/getting-started/using-unreal-engine) for general setup instructions. For mobile-specific configuration, you only need to configure your Discord Application ID in the project settings.
### Configure Discord Application ID
To enable mobile authentication in Unreal Engine:
1. Open **Project Settings** (`Edit -> Project Settings...`)
2. Search for `discord` in the search bar
3. Under the **Discord Social SDK** section, enter your **Discord Application ID**
This setting configures the custom URL scheme (`discord-YOUR_APP_ID`) for deep linking on both iOS and Android platforms automatically.
Following the Getting Started guide for Unreal Engine? Continue with [Step 6: Initialize the SDK](/developers/discord-social-sdk/getting-started/using-unreal-engine#discordlocalplayersubsystem).
***
## C++ Standalone Setup
The Discord Social SDK may be used as a C++ library in a standard iOS or Android project. Follow the steps below for your platform:
### iOS Setup
#### 1. Create and Configure Xcode Project
1. Create an Objective-C iOS project in Xcode
2. Add `discord_partner_sdk.xcframework` to your project
#### 2. Configure Build Settings
1. Add the xcframework to `Build Phases -> Link Binary with Libraries` if needed
2. In the `General` tab, under `Frameworks, Libraries and Embedded Content`:
* Set `discord_partner_sdk.xcframework` to **Embed & Sign**
#### 3. Configure Background Audio (Optional)
To maintain voice connectivity while backgrounded:
1. Configure background audio modes in your `Info.plist` using the `Signing & Capabilities` tab
2. Select *Audio, AirPlay, and Picture in Picture*, **not** Voice over IP
3. See [Configuring your app for Media Playback](https://developer.apple.com/documentation/avfoundation/configuring-your-app-for-media-playback?language=objc) for detailed instructions
#### 4. Register URL Scheme
To enable [`Client::Authorize`] support, register the appropriate URL scheme in your `Info.plist`:
```
CFBundleURLTypes
CFBundleURLSchemes
discord-YOUR_APP_ID
LSApplicationQueriesSchemes
discord
```
Replace `YOUR_APP_ID` with your application ID from the Discord Developer Portal (e.g., if your app ID is `123456`, register `discord-123456`).
#### 5. Include SDK Headers
In a C++ or Objective-C++ (`.mm`) source file:
```cpp theme={"system"}
#define DISCORDPP_IMPLEMENTATION // Define this in exactly ONE file
#include
```
`DISCORDPP_IMPLEMENTATION` must be defined in **exactly one file** in your codebase to expand necessary implementation code.
#### 6. Run Callbacks in Game Loop
In your main game loop, make sure to call:
```cpp theme={"system"}
discordpp::RunCallbacks();
```
### Android Setup
#### 1. Create Android Project
Create an Android game project based on the **Game Activity (C++)** template.
#### 2. Add SDK as Dependency
Add `discord_partner_sdk.aar` as a dependency in your Gradle project:
1. Add the AAR to a directory (e.g., `app/libs`)
2. In your `app/build.gradle`, add to dependencies:
```
dependencies {
implementation files("libs/discord_partner_sdk.aar")
}
```
#### 3. Enable Prefab
Ensure Prefab is enabled in your Gradle build. See [Native Dependencies in AARs](https://developer.android.com/build/native-dependencies#native-dependencies-aars) for details.
#### 4. Configure CMake
In your `CMakeLists.txt`:
```
find_package(discord_partner_sdk REQUIRED CONFIG)
target_link_libraries(your_target discord_partner_sdk::discord_partner_sdk)
```
#### 5. Include SDK Headers
In your C++ source:
```cpp theme={"system"}
#define DISCORDPP_IMPLEMENTATION // Define this in exactly ONE file
#include "discordpp.h"
```
`DISCORDPP_IMPLEMENTATION` must be defined in **exactly one file** in your codebase.
#### 6. Initialize SDK in Activity
In the `onCreate` method for your main activity (Java/Kotlin):
```
com.discord.socialsdk.DiscordSocialSdkInit.setEngineActivity(this);
```
#### 7. Run Callbacks in Game Loop
In your main C++ loop:
```cpp theme={"system"}
discordpp::RunCallbacks();
```
#### 8. Configure Authorization Support
To support [`Client::Authorize`]:
1. Add `androidx.browser` dependency (version 1.8 or later) to your `build.gradle`:
```
dependencies {
implementation 'androidx.browser:browser:1.8.0'
// Your other dependencies...
}
```
2. Add the appropriate `AndroidManifest.xml` activity registration:
```
```
Replace `YOUR_APP_ID` with your actual Discord application ID (e.g., `discord-1234567890123456789`).
Following the Getting Started guide for C++? Continue with [Step 9: Connect the SDK to Discord](/developers/discord-social-sdk/getting-started/using-c++#step-9-connect-the-sdk-to-discord).
***
## Understanding PKCE for Mobile
### What is PKCE?
PKCE (Proof Key for Code Exchange, pronounced "pixie") is a security extension to OAuth2 that prevents authorization code interception attacks. It adds an extra layer of security by requiring both the client and server to prove they're part of the same authentication flow.
### Why is PKCE Required on Mobile?
**Critical Requirement**: PKCE is **mandatory** for **all mobile applications** that use custom URL schemes (deep links), regardless of whether you're using a public client or a confidential client with server-side token exchange.
This is because custom URL schemes don't have the same security guarantees as HTTPS redirects, making them vulnerable to interception attacks without PKCE.
### How PKCE Works
1. **Client generates a code verifier**: A cryptographically random string
2. **Client creates a code challenge**: A hashed version of the verifier
3. **Client sends code challenge** with the authorization request
4. **Server stores the code challenge** with the authorization code
5. **Client sends code verifier** when exchanging the authorization code for a token
6. **Server verifies** that the verifier matches the stored challenge
The Discord Social SDK handles the generation of the code verifier and challenge for you via [`Client::CreateAuthorizationCodeVerifier`].
***
## Authentication Flow for Public Clients
This method requires enabling **Public Client** for your app. Most games will not want to ship with this enabled. [Learn more](/developers/discord-social-sdk/core-concepts/oauth2-scopes#oauth2-client-types)
If your app does not have a backend server and you've enabled `Public Client` in the Discord Developer Portal, you can use the SDK to handle the entire authentication flow, including PKCE.
### Step 1: Create Code Verifier and Challenge
Use [`Client::CreateAuthorizationCodeVerifier`] to generate the PKCE values:
```cpp theme={"system"}
// Generate code verifier and challenge
auto codeVerifier = client->CreateAuthorizationCodeVerifier();
```
### Step 2: Request Authorization
Use [`Client::Authorize`] with the code challenge:
```cpp theme={"system"}
discordpp::AuthorizationArgs args{};
args.SetClientId(YOUR_DISCORD_APPLICATION_ID);
args.SetScopes(discordpp::Client::GetDefaultPresenceScopes());
args.SetCodeChallenge(codeVerifier.Challenge());
client->Authorize(args, [client, codeVerifier](
discordpp::ClientResult result,
std::string code,
std::string redirectUri) {
if (!result.Successful()) {
std::cerr << "❌ Authorization Error: " << result.Error() << std::endl;
} else {
std::cout << "✅ Authorization successful! Exchanging code for token...\n";
// Proceed to Step 3
}
});
```
### Step 3: User Approval
After calling [`Client::Authorize`], the SDK will:
* Deep-link into the Discord mobile app if installed
* Or open a browser if Discord is not installed
* Present the authorization screen to the user
### Step 4: Exchange Authorization Code for Token
Once the user approves and your app receives the authorization code, exchange it for an access token using [`Client::GetToken`] with the code verifier:
```cpp theme={"system"}
client->GetToken(
YOUR_DISCORD_APPLICATION_ID,
code,
codeVerifier.Verifier(), // Critical: Pass the verifier
redirectUri,
[client](discordpp::ClientResult result,
std::string accessToken,
std::string refreshToken,
discordpp::AuthorizationTokenType tokenType,
int32_t expiresIn,
std::string scope) {
if (!result.Successful()) {
std::cerr << "❌ Error getting token: " << result.Error() << std::endl;
return;
}
std::cout << "🔓 Access token received! Establishing connection...\n";
// Update token and connect
client->UpdateToken(tokenType, accessToken, [client](discordpp::ClientResult result) {
client->Connect();
});
});
```
***
## Authentication Flow for Confidential Clients
If your application has a backend server and uses a confidential client (with client secret), you must still implement PKCE on mobile, but the token exchange happens on your server.
### Client-Side Implementation
#### Step 1: Create Code Verifier and Challenge
Generate the PKCE values on the client:
```cpp theme={"system"}
// Generate code verifier and challenge
auto codeVerifier = client->CreateAuthorizationCodeVerifier();
```
#### Step 2: Request Authorization
Pass the code challenge to the authorization request:
```cpp theme={"system"}
discordpp::AuthorizationArgs args{};
args.SetClientId(YOUR_DISCORD_APPLICATION_ID);
args.SetScopes(discordpp::Client::GetDefaultPresenceScopes());
args.SetCodeChallenge(codeVerifier.Challenge());
client->Authorize(args, [client, codeVerifier](
discordpp::ClientResult result,
std::string code,
std::string redirectUri) {
if (!result.Successful()) {
std::cerr << "❌ Authorization Error: " << result.Error() << std::endl;
} else {
std::cout << "✅ Authorization successful!\n";
// Send BOTH the authorization code AND the code verifier to your server
SendToServer(code, redirectUri, codeVerifier.Verifier());
}
});
```
**Critical**: You must send the **code verifier** (not the challenge) to your server along with the authorization code. The server needs the verifier to complete the token exchange.
### Server-Side Implementation
Your server must include the `code_verifier` parameter when exchanging the authorization code for an access token:
```python theme={"system"}
import requests
API_ENDPOINT = 'https://discord.com/api/v10'
CLIENT_ID = 'YOUR_CLIENT_ID'
CLIENT_SECRET = 'YOUR_CLIENT_SECRET'
def exchange_code(code, redirect_uri, code_verifier):
"""
Exchange authorization code for access token with PKCE verification.
IMPORTANT: The code_verifier parameter is REQUIRED for mobile apps,
even when using confidential clients with client secrets.
"""
data = {
'grant_type': 'authorization_code',
'code': code,
'redirect_uri': redirect_uri,
'code_verifier': code_verifier, # Required for mobile deep links
}
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
r = requests.post(
f'{API_ENDPOINT}/oauth2/token',
data=data,
headers=headers,
auth=(CLIENT_ID, CLIENT_SECRET)
)
r.raise_for_status()
return r.json()
```
#### Example Response
```json theme={"system"}
{
"access_token": "",
"token_type": "Bearer",
"expires_in": 604800,
"refresh_token": "",
"scope": "sdk.social_layer"
}
```
#### Client-Side: Update Token
Once your server returns the access token, update it in the SDK:
```cpp theme={"system"}
// Receive access token from your server
void OnTokenReceived(const std::string& accessToken) {
client->UpdateToken(
discordpp::AuthorizationTokenType::Bearer,
accessToken,
[client](discordpp::ClientResult result) {
if (result.Successful()) {
client->Connect();
}
});
}
```
***
## Working with Tokens
You'll want to store the access and refresh tokens for the player to use in future sessions.
Since `access_token`s generally expire after 7 days. You'll need to use the `refresh_token` to refresh the
player's access token, which is covered under [Refreshing Access Tokens](/developers/discord-social-sdk/development-guides/account-linking-with-discord#refreshing-access-tokens).
***
## Troubleshooting
### Common Issues
#### iOS: Authorization Opens Browser Instead of Discord App
**Problem**: The authorization flow opens a browser instead of deep-linking to the Discord mobile app.
**Solutions**:
1. Verify that `LSApplicationQueriesSchemes` includes `discord` in your Info.plist
2. Ensure the Discord mobile app is installed on the device
3. Check that your redirect URI is correctly configured as `discord-YOUR_APP_ID:/authorize/callback`
#### Android: Deep Link Not Working
**Problem**: After authorization, the app doesn't receive the callback.
**Solutions**:
1. Verify your AndroidManifest.xml has the correct intent filter
2. Ensure the `android:scheme` matches your redirect URI: `discord-YOUR_APP_ID`
3. Check that your activity is set to launch mode `singleTask` or `singleTop` if you need to handle multiple intents
#### Token Exchange Fails with "invalid\_grant"
**Problem**: Server-side token exchange returns an `invalid_grant` error.
**Solutions**:
1. **Most common**: You forgot to include the `code_verifier` parameter in the token exchange request
2. Verify you're sending the verifier (from `codeVerifier.Verifier()`), not the challenge
3. Ensure the code hasn't expired (codes are short-lived)
4. Check that the redirect URI matches exactly what was used in the authorization request
***
## Next Steps
Now that you've successfully implemented account linking on mobile, you can integrate more social features into your game.
}>
Design guidelines for account linking and user authentication
}>
Combine Discord and game friends into a single list for easy management.
}>
Display game status and information to Discord friends.
Need help? Join the [Discord Developers Server](https://discord.gg/discord-developers) and share questions in the `#social-sdk-dev-help` channel for support from the community.
If you encounter a bug while working with the Social SDK, please report it here: [https://dis.gd/social-sdk-bug-report](https://dis.gd/social-sdk-bug-report)
***
## Change Log
| Date | Changes |
| ---------------- | --------------- |
| January 26, 2026 | Initial release |
[Unity Sample Project]: https://discord.com/developers/applications/select/social-sdk/downloads
[`Client::Authorize`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ace94a58e27545a933d79db32b387a468
[`Client::CreateAuthorizationCodeVerifier`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aba6adc1f978e7bf4c5433c560e1ad704
[`Client::GetToken`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aaee636f91fb1ea3465157c20313b702c
# Account Linking from Your Game
Source: https://docs.discord.com/developers/discord-social-sdk/development-guides/account-linking-with-discord
Implement account linking to connect user accounts between your application and Discord.
## Overview
New to account linking? Read the [Account Linking overview](/developers/platform/account-linking) to understand what it is, how it works, and which flow to use.
This guide explains how to authenticate users with their existing Discord accounts via OAuth2, enabling seamless login and access to Discord features.
### Flexible Account Options
If a player does not have a Discord account, you can use the SDK to [create a provisional account](/developers/discord-social-sdk/development-guides/using-provisional-accounts) instead so that they can still access your game's features.
For the account linking integration flow we recommend — provisional account first, and Discord-linked
account second — see [Recommended Integration Path](#recommended-integration-path) below.
### Prerequisites
Before you begin, make sure you have:
* Read the [Core Concepts](/developers/discord-social-sdk/core-concepts) guide to understand:
* OAuth2 authentication flow
* Discord application setup
* SDK initialization
* Set up your development environment with:
* Discord application created in the [Developer Portal](https://discord.com/developers/applications)
* Discord Social SDK downloaded and configured
* Basic SDK integration working (initialization and connection)
If you haven't completed these prerequisites, we recommend first following the [Getting Started](/developers/discord-social-sdk/getting-started) guide.
This feature requires the **Default Presence Scopes** (`openid` and `sdk.social_layer_presence`).
Use [`Client::GetDefaultPresenceScopes`] when configuring your OAuth2 flow.
See the [OAuth2 Scopes guide](/developers/discord-social-sdk/core-concepts/oauth2-scopes) for details on all available scopes.
[`Client::GetDefaultPresenceScopes`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a7648bd1d2f7d9a86ebd0edb8bef12b5c
***
## Our Authentication Flow
OAuth2 is the standard authentication flow that allows users to sign in using their Discord account. The process follows these steps:
1. [Request authorization](/developers/discord-social-sdk/development-guides/account-linking-with-discord#step-1-request-authorization): Your game sends an authentication request to Discord.
2. [User Approval](/developers/discord-social-sdk/development-guides/account-linking-with-discord#step-2-user-approval): The user approves the request, granting access to your application.
3. [Receive Authorization Code](/developers/discord-social-sdk/development-guides/account-linking-with-discord#step-3-receiving-the-authorization-code): After approval, Discord redirects the user to your app with an authorization code.
4. [Exchange for Tokens](/developers/discord-social-sdk/development-guides/account-linking-with-discord#step-4-exchanging-the-authorization-code-for-an-access-token): The authorization code is exchanged for:
* Access Token, which is valid for \~7 days
* Refresh Token, used to obtain a new access token
The OAuth2 flow requires a user's account to be verified
### OAuth2 using the Discord Social SDK
* If the Discord client has [overlay support](https://support.discord.com/hc/en-us/articles/217659737-Game-Overlay-101) (Windows only), the OAuth2 login modal appears in your game instead of opening a browser.
* The SDK automatically handles redirects, simplifying the authentication flow.
* Some security measures, such as CSRF protection, are built-in, but you should always follow best practices to secure your app.
***
## Requesting Access Tokens
### Step 0: Configure OAuth2 Redirects
For OAuth2 to work correctly, you must **register the correct redirect URIs** for your app in the [**Discord Developer Portal**](https://discord.com/developers/applications/select/oauth2).
| Platform | Redirect URI |
| ----------- | ------------------------------------------------------------------------------------------ |
| **Desktop** | `http://127.0.0.1/callback` |
| **Mobile** | `discord-APP_ID:/authorize/callback` *(replace `APP_ID` with your Discord application ID)* |
### Step 1: Request Authorization
The SDK provides helper methods to simplify OAuth2 login.
Use the [`Client::Authorize`] method to initiate authorization and allow the user to approve access.
#### Authorization Scopes
One of the required arguments to [`Client::Authorize`] is scopes — the set of permissions your game is requesting from the user. The Discord Social SDK provides two helper methods that cover the most common use cases:
| Helper Method | Scopes Requested | Features Enabled |
| ----------------------------------------- | ---------------------------------- | --------------------------------------------------------------------------------- |
| [`Client::GetDefaultPresenceScopes`] | `openid sdk.social_layer_presence` | Account linking, friends list, rich presence |
| [`Client::GetDefaultCommunicationScopes`] | `openid sdk.social_layer` | All of the above, plus lobbies, voice chat, direct messaging, and linked channels |
Start with [`Client::GetDefaultPresenceScopes`] unless you know you need the communication features. You can always add more scopes later as your integration expands. See the [OAuth2 Scopes guide](/developers/discord-social-sdk/core-concepts/oauth2-scopes) for full details.
#### Authorization Code Verifier
If you are using [`Client::GetToken`] in [Step 4](/developers/discord-social-sdk/development-guides/account-linking-with-discord#step-4-exchanging-the-authorization-code-for-an-access-token), you will need to specify a "code challenge" and "code verifier" in your requests. We'll spare you the boring details of how that works (woo… crypto), as we've made a simple function to create these for you, [`Client::CreateAuthorizationCodeVerifier`], which you can use to generate the code challenge and verifier.
```cpp theme={"system"}
// Create a code verifier and challenge if using GetToken
auto codeVerifier = client->CreateAuthorizationCodeVerifier();
discordpp::AuthorizationArgs args{};
args.SetClientId(YOUR_DISCORD_APPLICATION_ID);
args.SetScopes(discordpp::Client::GetDefaultPresenceScopes());
args.SetCodeChallenge(codeVerifier.Challenge());
client->Authorize(args, [client, codeVerifier](discordpp::ClientResult result, std::string code, std::string redirectUri) {
if (!result.Successful()) {
std::cerr << "❌ Authorization Error: " << result.Error() << std::endl;
} else {
std::cout << "✅ Authorization successful! Next step: exchange code for an access token \n";
}
});
```
### Step 2: User Approval
After calling [`Client::Authorize`], the SDK will open a browser window, Discord client, or an in-game overlay to prompt the user to approve the request.
### Step 3: Receiving the Authorization Code
Once the user approves the request from Step 2, Discord will redirect the user back to your app with an authorization code that you can use to exchange for an access token.
### Step 4: Exchanging the Authorization Code for an Access Token
#### Server-to-Server Get Token Exchange
If your application uses a backend server and does **not** have `Public Client` enabled, you can manually exchange the authorization code for an access token using the Discord API.
```python theme={"system"}
import requests
API_ENDPOINT = 'https://discord.com/api/v10'
CLIENT_ID = 'YOUR_CLIENT_ID'
CLIENT_SECRET = 'YOUR_CLIENT_SECRET'
def exchange_code(code, redirect_uri, code_verifier):
data = {
'grant_type': 'authorization_code',
'code': code,
'redirect_uri': redirect_uri,
'code_verifier': code_verifier
}
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
r = requests.post(f'{API_ENDPOINT}/oauth2/token', data=data, headers=headers, auth=(CLIENT_ID, CLIENT_SECRET))
r.raise_for_status()
return r.json()
```
#### Example Response
```json theme={"system"}
{
"access_token": "",
"token_type": "Bearer",
"expires_in": 604800,
"refresh_token": "",
"scope": "sdk.social_layer"
}
```
#### Token Exchange for Public Clients
This method requires enabling **Public Client** for your app. Most games will not want to ship with this enabled. [Learn more](/developers/discord-social-sdk/core-concepts/oauth2-scopes#oauth2-client-types)
If your app does not have a backend server, enable `Public Client` in the Discord Developer Portal and use [`Client::GetToken`] to automatically exchange the authorization code for a token.
We will also need the code verifier used to generate the code challenge in Step 1.
```cpp theme={"system"}
client->GetToken(YOUR_DISCORD_APPLICATION_ID, code, codeVerifier.Verifier(), redirectUri,
[client](discordpp::ClientResult result,
std::string accessToken,
std::string refreshToken,
discordpp::AuthorizationTokenType tokenType,
int32_t expiresIn,
std::string scope) {
std::cout << "🔓 Access token received! Establishing connection...\n";
// Next step: Update the token in the client and connect to Discord
});
```
***
## Working with Tokens
Once you've received your access token, you'll want to set the token in the SDK. You can use [`Client::UpdateToken`] to do that. At this point, you're authorized and ready to go! You'll want to store the player's access token and refresh tokens somewhere.
Please note that the `access_token` values do expire. You'll need to use the `refresh_token` to refresh the player's access token.
```cpp theme={"system"}
client->UpdateToken(discordpp::AuthorizationTokenType::Bearer, ACCESS_TOKEN_VALUE, [client](discordpp::ClientResult result) {
client->Connect();
);
```
***
## Refreshing Access Tokens
Access tokens expire after 7 days, requiring refresh tokens to get a new one.
### Server-to-Server Token Refresh
If you're handling authentication on your server, send an API request to refresh the token.
```python theme={"system"}
import requests
API_ENDPOINT = 'https://discord.com/api/v10'
CLIENT_ID = 'YOUR_CLIENT_ID'
CLIENT_SECRET = 'YOUR_CLIENT_SECRET'
def refresh_token(refresh_token):
data = {
'grant_type': 'refresh_token',
'refresh_token': refresh_token
}
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
r = requests.post(f'{API_ENDPOINT}/oauth2/token', data=data, headers=headers, auth=(CLIENT_ID, CLIENT_SECRET))
r.raise_for_status()
return r.json()
```
### Refreshing Access Tokens for Public Clients
This method requires enabling **Public Client** for your app. Most games will not want to ship with this enabled. [Learn more](/developers/discord-social-sdk/core-concepts/oauth2-scopes#oauth2-client-types)
The easiest way to refresh tokens is using the SDK's [`Client::RefreshToken`] method.
```cpp theme={"system"}
client->RefreshToken(
YOUR_DISCORD_APPLICATION_ID, GetRefreshToken(),
[client](discordpp::ClientResult result, std::string accessToken,
std::string refreshToken,
discordpp::AuthorizationTokenType tokenType, int32_t expiresIn,
std::string scope) {
if (!result.Successful()) {
std::cout << "❌ Error refreshing token: " << result.Error()
<< std::endl;
return;
}
// Update token and connect
UpdateToken(client, refreshToken, accessToken);
});
```
### When Refresh Fails
A stored refresh token can become invalid between sessions for several reasons:
* The user revoked your application from their *User Settings → Authorized Apps* page.
* You called the [unmerge or token revocation endpoint](#revoking-access-tokens).
* The [user's Discord account was banned](/developers/discord-social-sdk/development-guides/using-provisional-accounts#ban-driven-unmerge).
* The access and refresh tokens expired or otherwise became invalid, with no revocation, unmerge, or ban involved.
In every one of these cases the response on `/oauth2/token` with `grant_type=refresh_token` is identical:
* **HTTP status:** `400`
* **Body:** `{ "error": "invalid_grant", "error_description": "Invalid \"refresh_token\" in request" }`
There is no ban-specific or unlink-specific error code on this path. `invalid_grant` only tells you the refresh token is no longer valid server-side — not *why* — so you cannot tell from the error alone which of the situations above you are in.
#### Recovering from `invalid_grant`
How you recover depends on whether the user still has a linked Discord account. Because the `invalid_grant` error doesn't distinguish the cases, the robust pattern is to **attempt the provisional account fallback first, then handle error `530010`**:
1. Drop the stored Discord tokens for the user and attempt the [provisional account flow](/developers/discord-social-sdk/development-guides/using-provisional-accounts#implementing-provisional-accounts).
2. **If the provisional flow succeeds**, a revocation, unmerge, or ban had already reverted the linked account to a [provisional account](#revoking-access-tokens). The player stays in-game even though their Discord link is gone.
3. **If the provisional flow returns error code `530010`** — `User account is non-provisional and should be authed through OAuth2` — the tokens simply expired or became invalid and the user **still has a linked Discord account**. The token-exchange endpoint deliberately blocks linked users from the provisional path. Prompt the player to re-run the [OAuth2 authorization flow](#step-1-request-authorization) to obtain fresh tokens instead.
To be notified when a user's authorization for your app is revoked — whether by an unlink, a token revocation, or a Discord account ban — integrate the [`APPLICATION_DEAUTHORIZED`](/developers/events/webhook-events#application-deauthorized) webhook event. That gives you a proactive signal you can act on immediately, rather than discovering the change the next time a refresh fails.
***
## Revoking Access Tokens
A user's authorization for your app can be revoked in several ways — by your own backend, by the user from Discord's UI, or by Discord itself when an account is banned. Regardless of how the revocation is triggered:
* The user's access and refresh tokens are immediately invalidated. Any subsequent refresh on `/oauth2/token` returns `HTTP 400` with `error: "invalid_grant"` (see [When Refresh Fails](#when-refresh-fails)).
* An [`APPLICATION_DEAUTHORIZED`](/developers/events/webhook-events#application-deauthorized) webhook is fired to your app.
For Discord Social SDK-integrated apps, every revocation path in this section is mechanically an [unmerge](/developers/discord-social-sdk/development-guides/using-provisional-accounts#unmerging-provisional-accounts): the merged Discord account reverts to a new provisional account carrying the original `external_auth_token`. This holds whether revocation comes from `/oauth2/token/revoke`, `Client::RevokeToken`, the user removing your app from Discord's UI, or a ban. Only [ban-driven unmerges](/developers/discord-social-sdk/development-guides/using-provisional-accounts#ban-driven-unmerge) create a *restricted* provisional account — the others create an unrestricted one that can be re-merged freely.
You'll naturally know about revocations your own code initiates. For revocations triggered outside your code — see [Out-of-Band Revocation](#out-of-band-revocation) below — the webhook is the only proactive way to find out.
When any valid access or refresh token is revoked, all of your application's access and refresh tokens for that user are immediately invalidated.
### Server-to-Server Token Revocation
If your application uses a backend server, you can revoke tokens by making an API request to Discord's token revocation endpoint.
```python theme={"system"}
import requests
API_ENDPOINT = 'https://discord.com/api/v10'
CLIENT_ID = 'YOUR_CLIENT_ID'
CLIENT_SECRET = 'YOUR_CLIENT_SECRET'
def revoke_token(access_or_refresh_token):
data = {'token': access_or_refresh_token}
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
r = requests.post(f'{API_ENDPOINT}/oauth2/token/revoke', data=data, headers=headers, auth=(CLIENT_ID, CLIENT_SECRET))
r.raise_for_status()
```
### Revoking Access Tokens for Public Clients
This method requires enabling **Public Client** for your app. Most games will not want to ship with this enabled. [Learn more](/developers/discord-social-sdk/core-concepts/oauth2-scopes#oauth2-client-types)
The easiest way to revoke tokens is using the SDK's `Client::RevokeToken` method. This will invalidate all access and refresh tokens for the user and they cannot be used again.
```cpp theme={"system"}
client->RevokeToken(YOUR_DISCORD_APPLICATION_ID,
accessToken, // Can also use refresh token
[](const discordpp::ClientResult &result) {
if (!result.Successful()) {
std::cout
<< "? Error revoking token: " << result.Error()
<< std::endl;
return;
}
std::cout
<< "? Token successfully revoked! User logged out."
<< std::endl;
// Handle successful logout (clear stored tokens,
// redirect to login, etc.)
});
```
### Out-of-Band Revocation
Two paths sever the user's Discord link without your code calling any unmerge or revoke endpoint:
* The user removes your app from their Discord *User Settings → Authorized Apps* page (a [user-initiated unmerge](/developers/discord-social-sdk/development-guides/using-provisional-accounts#out-of-band-unmerge)).
* The user's Discord account is banned (a [ban-driven unmerge](/developers/discord-social-sdk/development-guides/using-provisional-accounts#ban-driven-unmerge), with an additional cross-platform-restricted state on the new provisional account).
Subscribe to [`APPLICATION_DEAUTHORIZED`](/developers/events/webhook-events#application-deauthorized) to be notified when either happens. If you miss the webhook, the [next token use will fail](#when-refresh-fails) and your game should fall back to the [provisional account flow](/developers/discord-social-sdk/development-guides/using-provisional-accounts#implementing-provisional-accounts).
***
## Recommended Integration Path
The recommended path for integrating the Discord Social SDK is that your game has a primary authentication other than Discord that initially sets up a provisional account, and have the player link their Discord account to this primary authentication.
This approach protects your users' game access and data if they encounter issues with their Discord account, such as a permanent or temporary ban. To implement this recommended path:
1. Create an account through a [non-Discord authentication provider](/developers/discord-social-sdk/development-guides/using-provisional-accounts#configuring-your-identity-provider), and create a provisional account attached to it.
2. When users later authenticate through Discord to link their account, have your game back end execute the [merge their provisional account with their Discord Account](/developers/discord-social-sdk/development-guides/using-provisional-accounts#merging-provisional-accounts).
3. The account merging process will internally store the `externalAuthToken` from the provisional account against their Discord account. If a ban of the Discord account happens, that `externalAuthToken` will be attached to the new provisional account that is created in its stead, with the original Discord account's in-game friends, and will be available through the authentication provider the account was initially setup with.
4. As a last step, your game back end should maintain the record of the `externalAuthToken` against the user account, even after the account merging process, since it will be needed to [authenticate via a provisional account](/developers/discord-social-sdk/development-guides/using-provisional-accounts#implementing-provisional-accounts) should Discord authentication fails for a ban, or any other reason.
```mermaid theme={"system"}
sequenceDiagram
participant User
participant Game
participant NonDiscordAuth as Non-Discord Auth Provider
participant GameBackend as Game Backend
participant Discord
User->>Game: Start game
Game->>NonDiscordAuth: Create account
NonDiscordAuth-->>Game: Authentication successful
Game->>GameBackend: Request provisional account
GameBackend->>Discord: Create provisional account
Discord-->>GameBackend: Account created
GameBackend-->>Game: Return Provisional Account
Note over GameBackend: Store externalAuthToken
User->>Game: Choose to link Discord account
Game->>Discord: Request authentication
Discord-->>Game: Auth successful
Game->>GameBackend: Request account merge
GameBackend->>Discord: Merge Provisional Account
Note over Discord: Stores externalAuthToken against Discord account
Discord-->>GameBackend: Merge successful
Note over GameBackend: Maintain externalAuthToken record
alt Discord Authentication Fails (Ban or Other Issue)
User->>Game: Attempt to login
Game->>Discord: Request authentication
Discord-->>Game: Auth failed
Game->>NonDiscordAuth: Fallback authentication
NonDiscordAuth-->>Game: Authentication successful
Game->>GameBackend: Access provisional account with externalAuthToken
GameBackend-->>Game: Access granted with in-game friends list
Game-->>User: Access game
end
```
If you use Discord as the primary or sole authentication mechanism for your game, you risk players permanently losing access to their in-game data if their Discord account is banned, as there is no way to migrate them to a provisional account that is connected to an external authentication provider.
***
## Next Steps
Now that you've successfully implemented account linking with Discord, you can integrate more social features into your game.
}>
Design guidelines for account linking and user authentication
}>
Combine Discord and game friends into a single list for easy management.
}>
Display game status and information to Discord friends.
Need help? Join the [Discord Developers Server](https://discord.gg/discord-developers) and share questions in the `#social-sdk-dev-help` channel for support from the community.
If you encounter a bug while working with the Social SDK, please report it here: [https://dis.gd/social-sdk-bug-report](https://dis.gd/social-sdk-bug-report)
***
## Change Log
| Date | Changes |
| -------------- | ----------------------------------------------------------- |
| June 9, 2026 | Clarify `invalid_grant` recovery and the `530010` error |
| May 22, 2026 | Add out-of-band revocation and recommended integration path |
| March 17, 2025 | initial release |
[`Client::Authorize`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ace94a58e27545a933d79db32b387a468
[`Client::CreateAuthorizationCodeVerifier`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aba6adc1f978e7bf4c5433c560e1ad704
[`Client::GetDefaultCommunicationScopes`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a71499da752fbdc2d4326ae0fd36c0dd1
[`Client::GetDefaultPresenceScopes`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a7648bd1d2f7d9a86ebd0edb8bef12b5c
[`Client::GetToken`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aaee636f91fb1ea3465157c20313b702c
[`Client::RefreshToken`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a187af0f99f94b3b9a4ad4302f6a443e7
[`Client::UpdateToken`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a606b32cef7796f7fb91c2497bc31afc4
# Creating a Unified Friends List
Source: https://docs.discord.com/developers/discord-social-sdk/development-guides/creating-a-unified-friends-list
Build a unified friends list that combines Discord relationships with your application.
## Overview
A unified friends list combines both Discord and game-specific relationships in one view. This guide will show you how to:
* Fetch all relationship data
* Filter and organize relationships
* Display online status
* Handle different relationship types
### Prerequisites
Before you begin, make sure you have:
* Set up the Discord Social SDK with [Getting Started Guide](/developers/discord-social-sdk/getting-started)
* Authenticated your users with [Development Guide: Account Linking](/developers/discord-social-sdk/development-guides/account-linking-with-discord)
* Understanding of relationship types with [Development Guide: Managing Relationships in Your Game](/developers/discord-social-sdk/development-guides/managing-relationships)
This feature requires the **Default Presence Scopes** (`openid` and `sdk.social_layer_presence`).
Use [`Client::GetDefaultPresenceScopes`] when configuring your OAuth2 flow.
See the [OAuth2 Scopes guide](/developers/discord-social-sdk/core-concepts/oauth2-scopes) for details on all available scopes.
[`Client::GetDefaultPresenceScopes`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a7648bd1d2f7d9a86ebd0edb8bef12b5c
### Implementing a Unified Friends List
The Discord friend list is ultimately constructed from two entities: Relationships, and Users. You can query
Relationships API to find everyone a user is a friend with, and the Users API to find the necessary extra information
for rendering the list, such as whether they are online or not.
### Relationships
[Relationships](/developers/discord-social-sdk/development-guides/managing-relationships) are how Discord models friends,
friend requests, and more. All relationships for the current user are loaded when the Client connects. Each
relationship has a target user id, and a type, such as `Friend`, `PendingOutgoing`, or `Blocked`.
To allow users to manage their relationships in your game, you should provide a way to accept or reject friend
requests, block users, and manage pending requests. See [Development Guide: Managing Relationships in Your Game](/developers/discord-social-sdk/development-guides/managing-relationships) for implementation details.
### Users
Users are the Discord users that are part of the relationships. The SDK provides a way to fetch a user by their ID, and the user object contains information such as their username, display name, avatar, and more.
### User Status & Rich Presence
Presence is how Discord stores whether a user is currently online or not, as well as what activities they are currently doing (such as playing a game). The SDK gives you access to two types of status:
* Online Status: Online, Offline, Idle, etc.
* Rich Presence: Any activities associated with the current game (or application in Discord parlance).
The SDK will only display activities associated with the current game, meaning you will not be able to see other game activities in Rich Presence, even if you can see them in the Discord client.
See our [Design Guidelines: Status & Rich Presence](/developers/discord-social-sdk/design-guidelines/status-rich-presence) for best practices on displaying presence information.
There are two ways in which you can create a unified friends list in your game:
1. Using the SDK Unified Friends List helper functions, which automatically group and sort
relationships and users for you.
2. Directly retrieving relationships and users from the SDK, and sorting manually.
## Approach 1: Using SDK Unified Friends List Helper Functions
This approach is recommended as it significantly reduces the amount of code you need to write and maintain compared to
manually fetching and organizing relationships, while ensuring your friends list follows Discord's best practices.
The Discord Social SDK provides built-in helper functions that automatically group and sort your friends list according
to Discord's [recommended design guidelines](/developers/discord-social-sdk/design-guidelines/unified-friends-list).
This approach is generally simpler and more maintainable than manually fetching and organizing relationships.
The SDK automatically organizes friends into the three groups we find via [`RelationshipGroupType`]:
* `OnlinePlayingGame`: Friends who are online and currently playing your game
* `OnlineElsewhere`: Friends who are online but not playing your game
* `Offline`: Friends who are offline
### Step 1: Display the Unified Friends List
The [`Client::GetRelationshipsByGroup`] method returns a pre-sorted list of relationships for a specific group type.
This eliminates the need to manually filter, categorize, and sort friends yourself. The SDK handles all the logic
for determining which group each friend belongs to based on their online status and game activity, and
automatically sorts users within each group (for example, users who have played your game are moved to the top of
the OnlineElsewhere group).
Let's create a function that uses the SDK helper functions to display a properly organized friends list:
```cpp theme={"system"}
void DisplayUnifiedFriendsList(const std::shared_ptr &client) {
// Get friends playing the game
const auto onlineInGame = client->GetRelationshipsByGroup(
discordpp::RelationshipGroupType::OnlinePlayingGame
);
// Get friends online elsewhere
const auto onlineElsewhere = client->GetRelationshipsByGroup(
discordpp::RelationshipGroupType::OnlineElsewhere
);
// Get offline friends
const auto offline = client->GetRelationshipsByGroup(
discordpp::RelationshipGroupType::Offline
);
// Display "Online - GameTitle" Friends
std::cout << "\n=== Online - GameTitle (" << onlineInGame.size() << ") ===\n";
for (const auto& relationship : onlineInGame) {
auto user = relationship.User();
if (user) {
std::string displayStr = "🟣 " + user->DisplayName();
// Add Discord friend indicator
if (relationship.DiscordRelationshipType() == discordpp::RelationshipType::Friend) {
displayStr += " 👾";
}
// Add game friend indicator
if (relationship.GameRelationshipType() == discordpp::RelationshipType::Friend) {
displayStr += " 🎮";
}
std::cout << displayStr << "\n";
}
}
// Display "Online - Elsewhere" Friends
std::cout << "\n=== Online - Elsewhere (" << onlineElsewhere.size() << ") ===\n";
for (const auto& relationship : onlineElsewhere) {
auto user = relationship.User();
if (user) {
std::string displayStr = "🟢 " + user->DisplayName();
// Add Discord friend indicator
if (relationship.DiscordRelationshipType() == discordpp::RelationshipType::Friend) {
displayStr += " 👾";
}
// Add game friend indicator
if (relationship.GameRelationshipType() == discordpp::RelationshipType::Friend) {
displayStr += " 🎮";
}
std::cout << displayStr << "\n";
}
}
// Display "Offline" Friends
std::cout << "\n=== Offline (" << offline.size() << ") ===\n";
for (const auto& relationship : offline) {
auto user = relationship.User();
if (user) {
std::string displayStr = "⚫ " + user->DisplayName();
// Add Discord friend indicator
if (relationship.DiscordRelationshipType() == discordpp::RelationshipType::Friend) {
displayStr += " 👾";
}
// Add game friend indicator
if (relationship.GameRelationshipType() == discordpp::RelationshipType::Friend) {
displayStr += " 🎮";
}
std::cout << displayStr << "\n";
}
}
}
```
### Step 2: Set Up Automatic Updates
To keep your friends list up-to-date automatically, use the [`Client::SetRelationshipGroupsUpdatedCallback`]. This
callback is triggered whenever any change occurs that might affect the friends list grouping, such as a friend going
online or offline, or when a relationship changes, such as when you accept a friend request, or block a user.
```cpp theme={"system"}
// Set up the unified friends list update callback
client->SetRelationshipGroupsUpdatedCallback([&client](const uint64_t userId) {
std::cout << "👥 Friends list updated for user: " << userId << std::endl;
DisplayUnifiedFriendsList(client);
});
```
***
## Approach 2: Manually Fetching Relationships and Users
In this section we'll show a more manual method which gives you more control over how the friends list is displayed in your game.
### Step 1: Fetch Relationships
First, let's create a function that utilises [`Client::GetRelationships`] to query all the relationships and
user information we for our account:
```cpp theme={"system"}
void DisplayFriendsList(discordpp::Client& client) {
std::vector relationships{};
for (auto& relationship: client->GetRelationships()) {
auto user = relationship.User();
if (!user) {
continue;
}
std::string str{};
// Identifying information about the user:
str += " DiscordName: " + user->DisplayName();
str += " DiscordId: " + std::to_string(user->Id());
// Provisional users don't have a Discord icon shown next to them:
str += " IsProvisional: " + std::to_string(user->IsProvisional());
// Whether the relationship is for a friend, a friend request, or because the user is blocked:
// For a friends list you'll want to filter out blocked users
// And likely display friend requests in a different section
str += " DiscordRelationshipType: " + std::string(discordpp::EnumToString(relationship.DiscordRelationshipType()));
str += " GameRelationshipType: " + std::string(discordpp::EnumToString(relationship.GameRelationshipType()));
// Whether the user is online/offline/etc:
str += " IsOnlineAnywhere: " + std::to_string(user->Status() != discordpp::StatusType::Offline);
str += " IsOnlineInGame: " + std::to_string(user->GameActivity() != std::nullopt);
relationships.push_back(str);
}
std::sort(relationships.begin(), relationships.end());
for (auto str : relationships) {
printf("%s\n", str.c_str());
}
}
```
The `relationship.User()` function returns a [`UserHandle`] object that represents a user that the Discord Social SDK
knows about.
Handle objects maintain references to both the underlying data and the SDK instance, which means that when their
data changes, existing handle objects automatically reflect these changes without needing to be recreated.
We will want to call this function when the client is ready, so let's add it to our ready callback:
```cpp theme={"system"}
// Set up status callback to monitor client connection
client->SetStatusChangedCallback([client](discordpp::Client::Status status, discordpp::Client::Error error, int32_t errorDetail) {
std::cout << "🔄 Status changed: " << discordpp::Client::StatusToString(status) << std::endl;
if (status == discordpp::Client::Status::Ready) {
std::cout << "✅ Client is ready! You can now call SDK functions.\n";
std::cout << "👥 Friends Count: " << client->GetRelationships().size() << std::endl;
SetRichPresence(client);
DisplayFriendsList(client);
} else if (error != discordpp::Client::Error::None) {
std::cerr << "❌ Connection Error: " << discordpp::Client::ErrorToString(error) << " - Details: " << errorDetail << std::endl;
}
});
```
This will output the raw relationship data to the console. You can use this information to filter, organize and build a friends list that fits your game's design aesthetic.
***
### Step 2: Organize Relationships
Based on our design guidelines for a [Unified Friends List](/developers/discord-social-sdk/design-guidelines/unified-friends-list), you should separate the player's friends list into three sections: `Online - GameTitle`, `Online - Elsewhere`, and `Offline`.
Because we are building a text console application, we will use emojis to represent the status of each friend but you can use your own design elements to convey status and presence in your game.
Let's update our `DisplayFriendsList` function to reflect our three sections and categorize friends based on their status:
* We will create three vectors to store the friends in each category.
* We will filter out pending friends and blocked users.
* We will add indicators for Discord friends, game friends, and provisional users.
* We will categorize friends based on their game and presence status.
* We will sort each category alphabetically.
* We will display each category separately.
This example is for reference only. Please make sure you use efficient data structures and cache data when appropriate to avoid performance issues in your game.
```cpp theme={"system"}
void DisplayFriendsList(std::shared_ptr client) {
// Create vectors for each section
std::vector inGame;
std::vector online;
std::vector offline;
for (auto& relationship : client->GetRelationships()) {
auto user = relationship.User();
if (!user) {
continue;
}
// Filter out pending friends and blocked users
// You can display friend requests and blocked users in a different view to allow players to manage them in your game
if (relationship.DiscordRelationshipType() != discordpp::RelationshipType::Friend) {
continue;
}
std::string str;
str += user->DisplayName();
// Add Discord friend indicator
// In a real game, please use the official Discord logo available in our design guidelines
if (relationship.DiscordRelationshipType() == discordpp::RelationshipType::Friend) {
str += " 👾";
}
// Add game friend indicator
if (relationship.GameRelationshipType() == discordpp::RelationshipType::Friend) {
str += " 🎮";
}
// Add provisional indicator
if (user->IsProvisional()) {
str += " (Provisional)";
}
// Categorize based on status
if (user->GameActivity()) {
// in game
inGame.push_back("🟣 " + str);
} else if (user->Status() != discordpp::StatusType::Offline) {
// online
online.push_back("🟢 " + str);
} else {
// offline
offline.push_back("⚫ " + str);
}
}
// Sort each category
std::sort(inGame.begin(), inGame.end());
std::sort(online.begin(), online.end());
std::sort(offline.begin(), offline.end());
// Display "Online - GameTitle" Friends
std::cout << "\n=== Online - GameTitle (" << inGame.size() << ") ===\n";
for (const auto& str : inGame) {
std::cout << str << "\n";
}
// Display "Online - Elsewhere" Friends
std::cout << "\n=== Online - Elsewhere (" << online.size() << ") ===\n";
for (const auto& str : online) {
std::cout << str << "\n";
}
// Display "Offline" Friends
std::cout << "\n=== Offline (" << offline.size() << ") ===\n";
for (const auto& str : offline) {
std::cout << str << "\n";
}
}
```
If we build and run our application, we should now see a list of friends separated into three categories: `Online - GameTitle`, `Online - Elsewhere`, and `Offline`.
### Step 3: Monitor Changes to Users
To monitor for user changes, we're going using the [`Client::SetUserUpdatedCallback`] function.
This callback will be triggered whenever a user's info is updated, such as name or presence changes (when they go online, offline, or start playing your game).
```cpp theme={"system"}
client->SetUserUpdatedCallback([&client](uint64_t userId) {
std::cout << "👤 User updated: " << userId << std::endl;
DisplayFriendsList(*client);
});
```
Now your friends list will automatically update when the presence of a friend changes, such as when they go online, offline or start playing your game.
The automatic updates of the [`UserHandle`] object to the latest the user information should be sufficient for
retrieving the most up-to-date user information.
[`Client::SetUserUpdatedCallback`] may be more useful to identify times when you wish to re-sort your
user list, or similar operations.
***
### Step 4: Monitor Changes in Relationships
Let us setup two callbacks to handle relationship updates.
These examples rebuild the friends list from scratch every time a relationship changes. For performance reasons, we
recommend maintaining a collection of [`UserHandle`] objects and adding and removing them appropriately.
#### Relationship Created Callback
This can happen when a user sends or accepts a friend invite, or blocks a user.
```cpp theme={"system"}
client->SetRelationshipCreatedCallback([&client](uint64_t userId, bool isDiscordRelationshipUpdate) {
std::optional user = client->GetUser(userId);
// if the userid is valid (which it should be), we can display the user's display name
if(user) {
std::cout << "🤝 Relationship created: " << user->DisplayName() << std::endl;
DisplayFriendsList(*client);
}
});
```
#### Relationship Deleted Callback
This can happen when a user rejects a friend request or removes a friend.
```cpp theme={"system"}
client->SetRelationshipDeletedCallback([&client](uint64_t userId, bool isDiscordRelationshipUpdate) {
std::cout << "🔥 Relationship deleted: " << userId << std::endl;
DisplayFriendsList(*client);
});
```
Now your friends list will automatically update when relationships change, such as when you add a new friend, accept a friend request, or block a user.
***
## Next Steps
Now that you have a unified friends list, you can build on your social features by allowing players to manage relationships, send game invites, and more. Check out our other guides for more information:
}>
Best practices for designing your Unified Friends List.
}>
Allow players to manage their friends, friend requests, and blocked users.
}>
Allow players to invite friends to join their game session or party.
Need help? Join the [Discord Developers Server](https://discord.gg/discord-developers) and share questions in the `#social-sdk-dev-help` channel for support from the community.
If you encounter a bug while working with the Social SDK, please report it here: [https://dis.gd/social-sdk-bug-report](https://dis.gd/social-sdk-bug-report)
***
## Change Log
| Date | Changes |
| -------------- | ---------------------- |
| March 17, 2025 | Initial release |
| July 17, 2025 | Add UFL helper methods |
[`Client::GetRelationships`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ad481849835cd570f0e03adafcf90125d
[`Client::GetRelationshipsByGroup`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a9f7898d3f3d1ec92b06c662df70746d5
[`Client::SetRelationshipGroupsUpdatedCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#af12441ef091298f968075b7190851098
[`Client::SetUserUpdatedCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a3559f375165acedc6d6677ef599b3a4a
[`RelationshipGroupType`]: https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#a503ed2f7b0bfbd435321a0e8b1dfba35
[`UserHandle`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserHandle.html#a587bcc838e42dc5c56f840a350070707
# Linked Channels
Source: https://docs.discord.com/developers/discord-social-sdk/development-guides/linked-channels
Connect Discord channels with your application for seamless communication.
This feature is currently available with
[limited access](/developers/discord-social-sdk/core-concepts/communication-features#limited-access). To apply for full access
to closed beta features, or to reach out to Discord directly to discuss your game, please fill out
[this form](https://discord.com/developers/social-sdk-closed-beta-access-request-form).
## Overview
Linked Channels let players connect in-game lobbies with Discord text channels, enabling chat between your game and Discord servers.
When linked:
* In-game messages appear in the Discord channel
* Discord messages appear in your game
* Messages from your game show your game's name and icon in Discord
### Prerequisites
Before implementing Linked Channels, make sure you have:
* [Set up the Discord Social SDK](/developers/discord-social-sdk/getting-started)
* An understanding of [Creating and Managing Lobbies](/developers/discord-social-sdk/development-guides/managing-lobbies)
To utilize this communication feature, you must enable [`Client::GetDefaultCommunicationScopes`] in your OAuth Scope configuration.
See the [OAuth Scopes Core Concepts Guide](/developers/discord-social-sdk/core-concepts/oauth2-scopes) for more details.
[`Client::GetDefaultCommunicationScopes`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a71499da752fbdc2d4326ae0fd36c0dd1
***
## Linked Channel Requirements
### Channel Requirements
For a Discord channel to be linkable with your game lobby, it must meet these requirements:
#### Channel Type and Settings
| Requirement | Description |
| --------------- | ------------------------------------------------------------------------------------------------- |
| Channel Type | Must be a Discord text channel in a server |
| Age Restriction | Cannot be an [age-restricted channel](https://support.discord.com/hc/en-us/articles/115000084051) |
| Link Status | Cannot be currently linked to another lobby |
#### Required Permissions
The user linking the channel must have these Discord permissions:
| Permission | Description |
| --------------- | -------------------------------- |
| Manage Channels | Ability to edit channel settings |
| View Channel | Can see and read the channel |
| Send Messages | Can post messages in the channel |
### Private Channels
Discord allows all channels the user can access in a server to be linked in game, even if that channel is private to other server members. This means a user could choose to link a private "admins chat" channel (assuming they are an admin) in the game if they wanted.
It's not possible for the game to know which users should have access to that channel. Every lobby member can view and reply to all messages sent in the linked channel.
If you are going to allow private channels to be linked in-game, you must make sure that users are aware that their private channel will be viewable by everyone in the lobby.
To help you identify which channels are public or private, we have added a `isViewableAndWriteableByAllMembers` boolean. You can use that boolean to prevent private channels from being linked or to know when to show a clear warning; it's up to you!
### User Requirements
#### Linking A Lobby
The lobby member must have the `CanLinkLobby` flag set to link a channel to a lobby. This flag is disabled by default and must be explicitly set using the [Lobby API](/developers/resources/lobby) for users you want elevated permissions. We recommend only toggling this on for the equivalent of the administrator/owner of a lobby.
This allows you, as the game developer, to say, "Only the admins of this guild are allowed to configure the linked channel."
#### Unlinking a Lobby
To unlink a channel from a lobby, lobby members only require the `CanLinkLobby` flag to be set for them. They **do not** need to have any permissions on the Discord side.
### Lobby Requirements
We recommend only using channel linking for **persistent** lobbies. Ephemeral lobbies such as match-chat are not good candidates for linking.
Lobbies created on the client side with secrets are also not eligible for channel linking.
***
## Creating a Linked Channel
Channel linking happens entirely in the game. The user is never kicked out of the game to go to Discord to set it up. So, at a high level, you will need to fetch the list of servers (also known as guilds) and channels the user can access, show them some UI to pick a channel, and then save that selection back to Discord.
### Fetch Available Servers (Guilds) and Channels
We provide a few helper methods in the SDK to fetch the guilds and channels to populate the lists.
[`Client::GetUserGuilds`] will fetch the user's Discord servers from our API. A list of `GuildMinimal` structs, including the guild ID and name, is returned on success.
[`Client::GetGuildChannels`] takes a guildId and fetches all the channels in a single guild that the user can access. On success, returns a list of `GuildChannel` objects, which are shown below:
```cpp theme={"system"}
struct GuildChannel {
struct LinkedLobby {
uint64_t applicationId;
uint64_t lobbyId;
};
uint64_t id;
std::string name;
bool isLinkable;
bool isViewableAndWriteableByAllMembers;
std::optional linkedLobby;
};
```
* `isLinkable` indicates whether the user can link the given guild channel. This includes checking for channel validity (Is it a text channel? Is it age-restricted? Is it already linked to another lobby?) and validating the user's channel permissions. (Check the **Edit Lobby/Channel Link** endpoint docs for a full list of requirements)
* `isViewableAndWriteableByAllMembers` indicates if the channel in Discord has restrictions on any members' or roles' ability to read or write to it (e.g. the channel may be considered private in some way). These read/write permissions are only enforced in the Discord client. A player who may not be able to view the channel in-Discord can read/write to it in-game as long as they are a lobby member. **It is important to notify the player performing the link that the channel contents may become exposed to players in-game who do not have access to the channel in Discord.**
### Saving a channel link selection
Once the user has chosen a channel to link to, you can call [`Client::LinkChannelToLobby`] to set or change the channel a lobby is linked to. You can also use [`Client::UnlinkChannelFromLobby`] to remove the link. The conditions specified in [Linked Channel Requirements](/developers/discord-social-sdk/development-guides/linked-channels#channel-requirements) must be met for the given lobby, channel, and user.
### Try It Out
To test Linked Channels before building out a player interface, here are some steps you can follow to get things working in a prototype using the Discord API:
1. Create a lobby using the **[Create Lobby](/developers/resources/lobby#create-lobby)** endpoint.
2. Enable the `CanLinkLobby` flag (`1 << 0`) on your lobby member by either sending a request to the `/lobbies//members/` endpoint or by including the member data in the body of the [Create Lobby](/developers/resources/lobby#create-lobby) request.
3. Identify a Discord server text channel that your `lobby_member` has the specified permissions enabled for (again, read/write and manage channels) and grab the channel's id.
4. Send a request to the `/lobbies//channel-linking` endpoint described above with the channel id.
Success! Your lobby and channel should now be linked, and if you send a message in the lobby you should see it appear in the channel and vice versa.
***
## Example Implementation
### Step 1: Set Up Message Handling
```cpp theme={"system"}
// filepath: your_game/channel_linking.cpp
void HandleDiscordMessage(const std::string& message) {
std::cout << "📱 Game received: " << message << "\n";
// Check if message is from the lobby
// Display in your game UI
}
void SetupMessageHandlers(std::shared_ptr client) {
client->SetMessageCreatedCallback([](uint64_t messageId) {
discordpp::MessageHandle message = client->GetMessageHandle(messageId);
HandleDiscordMessage(message);
});
}
```
### Step 2: Fetch Available Servers
```cpp theme={"system"}
void FetchDiscordServers(std::shared_ptr client) {
client->GetUserGuilds([](auto result, const auto& guilds) {
if (!result.Successful()) {
std::cout << "❌ Failed to fetch guilds\n";
return;
}
for (const auto& guild : guilds) {
std::cout << "📁 Guild: " << guild.name << "\n";
// Store guild IDs for later use
SaveGuildId(guild.id);
}
});
}
void FetchChannelsForGuild(
std::shared_ptr client,
uint64_t guildId
) {
client->GetGuildChannels(guildId, [](auto result, const auto& channels) {
if (!result.Successful()) return;
for (const auto& channel : channels) {
if (channel.isLinkable) {
std::cout << " 📝 Channel: " << channel.name;
if (!channel.isViewableAndWriteableByAllMembers) {
std::cout << " (Private)";
}
std::cout << "\n";
}
}
});
}
```
### Step 3: Link Channel to Lobby
```cpp theme={"system"}
void ShowPrivateChannelWarning() {
std::cout << "⚠️ Warning: This is a private channel.\n";
std::cout << "All lobby members will be able to see messages.\n";
std::cout << "Do you want to continue? (y/n)\n";
char response;
std::cin >> response;
return (response == 'y' || response == 'Y');
}
void LinkChannelToLobby(
std::shared_ptr client,
uint64_t lobbyId,
uint64_t channelId,
bool isPrivate
) {
if (isPrivate && !ShowPrivateChannelWarning()) {
std::cout << "❌ Channel linking cancelled\n";
return;
}
client->LinkChannelToLobby(
lobbyId,
channelId,
[](auto result) {
if (result.Successful()) {
std::cout << "✅ Channel linked successfully!\n";
} else {
std::cout << "❌ Failed to link channel\n";
}
}
);
}
```
### Step 4: Send and Receive Messages
```cpp theme={"system"}
void SendLobbyMessage(
std::shared_ptr client,
uint64_t lobbyId,
const std::string& message
) {
client->SendLobbyMessage(lobbyId, message, [](auto result) {
if (!result.Successful()) {
std::cout << "❌ Failed to send message\n";
}
});
}
void UnlinkChannel(
std::shared_ptr client,
uint64_t lobbyId
) {
client->UnlinkChannelFromLobby(lobbyId, [](auto result) {
if (result.Successful()) {
std::cout << "✅ Channel unlinked\n";
}
});
}
```
### Step 5: Putting It All Together
Here's how you might use these functions together:
```cpp theme={"system"}
// filepath: your_game/main.cpp
int main() {
auto client = std::make_shared();
// Set up handlers
SetupMessageHandlers(client);
// When user wants to link a channel:
FetchDiscordServers(client);
// After user selects a guild:
FetchChannelsForGuild(client, selectedGuildId);
// After user selects a channel:
LinkChannelToLobby(
client,
currentLobbyId,
selectedChannelId,
isPrivateChannel
);
// When sending a message:
SendLobbyMessage(client, currentLobbyId, "Hello from the game!");
// When done with the channel:
UnlinkChannel(client, currentLobbyId);
return 0;
}
```
## Joining Discord Servers via Linked Lobbies
Once a lobby is linked to a Discord channel, players can join the associated Discord server directly from your game. This feature simplifies the process of getting players into your Discord community by generating invites on-demand, eliminating the need to manually share invite links.
The [`Client::JoinLinkedLobbyGuild`] function generates a one-time-use invite for the current user and on supported
platforms, automatically navigates them to Discord to accept it.
For example, an in-game player flow could look like:
1. A Discord server admin links a channel to your game's lobby
2. Players in the lobby see an option to "Join Discord Server" in your game
3. When clicked, the SDK generates a unique invite and opens Discord
4. The player accepts the invite and becomes a Discord server member
Only players with linked Discord accounts can join the server. If a player is using a provisional account, you should prompt them to link their Discord account first.
```cpp theme={"system"}
const uint64_t lobbyId = 1234567890;
// Invite the user to join the Discord guild associated with the linked lobby
client->JoinLinkedLobbyGuild(
lobbyId,
// This is triggered when the user is using a provisional account, since
// the user needs a real Discord account to join the Discord server, so you don't need
// to implement a provisional user check on implementation.
[] {
// Show a message in your UI explaining that they need to link their Discord account
// and/or step them through the process
std::cout << "📝 User needs to link their Discord account\n";
},
// Called after the invite generation attempt completes, providing either a
// successful result with the invite URL or an error if the operation failed.
[](const discordpp::ClientResult &result, const std::string& inviteUrl) {
if(result.Successful()) {
std::cout << "✅ Discord invite generated successfully!\n";
// On console platforms, you'll need to display the invite URL
// for users to manually navigate to
#ifdef CONSOLE_PLATFORM
std::cout << "Join the Discord server at: " << inviteUrl << "\n";
// Display this URL in your game's UI for the player to use
#endif
} else {
std::cerr << "❌ Failed to generate Discord invite\n";
}
}
);
```
### Platform Considerations
* **Desktop**: The SDK automatically opens the Discord client or web browser with the invite
* **Console**: Since console platforms cannot navigate to Discord directly, you should display the invite URL in your game's UI so players can use it on another device
Discord server admins cannot restrict who can join the server via this method. Any player in a linked lobby can generate an invitation to the server, regardless of their lobby permissions.
Make sure to make your players aware to only link channels in servers you trust your players to join, and/or provide in-game options to disable this feature for certain lobbies.
***
## Next Steps
}>
Combine Discord and game friends into a single list for easy management.
}>
Display game status and information to Discord friends.
}>
Allow players to invite friends to join their game session or party.
Need help? Join the [Discord Developers Server](https://discord.gg/discord-developers) and share questions in the `#social-sdk-dev-help` channel for support from the community.
If you encounter a bug while working with the Social SDK, please report it here: [https://dis.gd/social-sdk-bug-report](https://dis.gd/social-sdk-bug-report)
***
## Change Log
| Date | Changes |
| -------------- | -------------------------------- |
| June 30, 2025 | Add communications scope warning |
| March 17, 2025 | initial release |
[`Client::GetGuildChannels`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#adba1e5a83c219a9c4f6dab1657778017
[`Client::GetUserGuilds`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aac1ec02df6074ed9213ce230e6a42fe1
[`Client::JoinLinkedLobbyGuild`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a54ec764e72e168de419ac14e24e8fc60
[`Client::LinkChannelToLobby`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a3114d58d50d4d2cb5752d95e121315d4
[`Client::UnlinkChannelFromLobby`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a28f78a6fe46eb11eb54ee9b53fa94ffe
# Managing Game Invites
Source: https://docs.discord.com/developers/discord-social-sdk/development-guides/managing-game-invites
Create and manage game invitations through Discord to bring friends into your application.
## Overview
Game Invites allow users to invite others to join their game session or party. This feature is available on the
Discord client and the Social SDK.
**Game Invites are not a standalone feature** - they are **powered entirely by Rich Presence**. When you configure Rich
Presence with party information, a join secret, and/or supported platforms, Discord automatically enables invite
functionality. This guide shows you how to configure Rich Presence to unlock game invites.
### Prerequisites
Before you begin, make sure you have:
* Completed the [Setting Rich Presence](/developers/discord-social-sdk/development-guides/setting-rich-presence) guide
* Understanding that without an active Rich Presence with party data, invites will not work
* Set up the Discord Social SDK with our [Getting Started guide](/developers/discord-social-sdk/getting-started)
To utilize this communication feature, you must enable [`Client::GetDefaultCommunicationScopes`] in your OAuth Scope configuration.
See the [OAuth Scopes Core Concepts Guide](/developers/discord-social-sdk/core-concepts/oauth2-scopes) for more details.
[`Client::GetDefaultCommunicationScopes`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a71499da752fbdc2d4326ae0fd36c0dd1
***
## Configuring Rich Presence to Enable Game Invites
Game invites, or activity invites, are **powered by rich presence**.
We covered the basics of [Setting Rich Presence](/developers/discord-social-sdk/development-guides/setting-rich-presence) in a previous guide but let's go over the key points again.
Let's talk about the naming of some Discord primitives first. Rich Presence, aka "Activity", can be thought of as the "current activity of a user" and is represented by the [`Activity`] class in the SDK and [in our gateway events](/developers/events/gateway-events#activity-object). This is not to be confused with [Discord Activities](/developers/activities/overview), which are embedded games that can also set and display rich presence.
### Setting Up Rich Presence
Below is an example of setting up rich presence in your game to be used with game invites.
```cpp theme={"system"}
// Create discordpp::Activity - This Rich Presence Activity is your invite configuration
discordpp::Activity activity;
activity.SetType(discordpp::ActivityTypes::Playing);
// Set the game state and details
activity.SetState("In Competitive Match");
activity.SetDetails("Valhalla");
// Update Rich Presence presence
client->UpdateRichPresence(activity, [](discordpp::ClientResult result) {
if(result.Successful()) {
std::cout << "🎮 Rich Presence updated successfully!\n";
// Note: Invites are NOT yet enabled - we need party info and join secret
} else {
std::cerr << "❌ Rich Presence update failed";
}
});
```
If we run our game, the Discord client will show we are "In Competitive Match" on "Valhalla".
You must set up your rich presence [`Activity`] with party information and a join secret to send game invites. Let's do that next.
### Adding an Activity Party
```cpp theme={"system"}
// rest of the code
// Create discordpp::ActivityParty
discordpp::ActivityParty party;
party.SetId("party1234");
// current party size
party.SetCurrentSize(1);
// max party size
party.SetMaxSize(5);
// Set the party information in the Activity, to inform the invite about the party size and how many players can join
activity.SetParty(party);
// Update Rich Presence
// Still not enough for invites - we need the join secret!
```
If we run our game, the Discord client will show that we are "In Competitive Match" on "Valhalla" with more information about the party.
We're almost there! Let's add the join secret next so we can send and receive game invites.
### Adding Join Secret & Supported Platforms
The last step is to add a join secret to your rich presence activity.
The `Join Secret` is a generic secret that you can use to [join a Discord lobby](/developers/discord-social-sdk/development-guides/managing-lobbies), a game session, or something else. The game invite system is a way for players to share this secret value with other players - how you use it is up to you.
You will also need to set the supported platforms for joining the game so that the Discord client can display the correct invite button for the user's platform.
```cpp theme={"system"}
// Create discordpp::Activity
// Create ActivitySecrets
discordpp::ActivitySecrets secrets;
secrets.SetJoin("joinsecret1234"); // Rich Presence secret will be in the invite payload
activity.SetSecrets(secrets);
// Set supported platforms that can join the game
// See discordpp::ActivityGamePlatforms for available platforms
activity.SetSupportedPlatforms(discordpp::ActivityGamePlatforms::Desktop);
// Update Rich Presence
// ✅ NOW invites are enabled through Rich Presence!
```
### Putting It All Together
```cpp theme={"system"}
// Create discordpp::Activity - This Rich Presence Activity is your invite configuration
discordpp::Activity activity;
activity.SetType(discordpp::ActivityTypes::Playing);
// Set the game state and details
activity.SetState("In Competitive Match");
activity.SetDetails("Valhalla");
// Set the party information
discordpp::ActivityParty party;
party.SetId("party1234");
party.SetCurrentSize(1); // current party size
party.SetMaxSize(5); // max party size
// Set the party information in the Activity, to inform the invite about the party size and how many players can join
activity.SetParty(party);
// Create ActivitySecrets
discordpp::ActivitySecrets secrets;
secrets.SetJoin("joinsecret1234"); // Rich Presence secret will be in the invite payload
activity.SetSecrets(secrets);
// Set supported platforms that can join the game
// See discordpp::ActivityGamePlatforms for available platforms
activity.SetSupportedPlatforms(discordpp::ActivityGamePlatforms::Desktop);
// Update Rich Presence presence
client->UpdateRichPresence(activity, [](discordpp::ClientResult result) {
if(result.Successful()) {
std::cout << "🎮 Rich Presence updated successfully!\n";
// ✅ Rich Presence updated = Game invites now available!
} else {
std::cerr << "❌ Rich Presence update failed";
// ❌ No Rich Presence = No invites possible
}
});
```
Now your game supports rich presence with game invites! The Discord client will show an invite button to your friends when they see your rich presence.
***
## Registering a Launch Command
Before we send a game invite, let's make sure that Discord knows how to launch your game when someone opens an invite.
Launch registration is **local**, as in it only affects the machine that calls it.
Therefore, each time the SDK starts up within your game client, register how your game should be launched on that machine,
so that if the player opens an invite from Discord, your game can be launched for them if not already running.
There are two ways to register, depending on how your game is distributed:
* Register a launch command for your game
* Register a Steam Game ID
### Registering a Launch Command
[`Client::RegisterLaunchCommand`] allows you to register a command that Discord will run on this machine to launch your game. Run this when the SDK starts up so that if the user opens an invite from Discord the game can be launched for them.
```cpp theme={"system"}
client->RegisterLaunchCommand(YOUR_APP_ID, "yourgame://");
```
### Registering a Steam Game
For Steam games, [`Client::RegisterLaunchSteamApplication`] allows you to register your Steam game ID on this machine. As with the launch command, run this when the SDK starts up so that if the user opens an invite from Discord the game can be launched for them.
```cpp theme={"system"}
client->RegisterLaunchSteamApplication(YOUR_APP_ID, STEAM_GAME_ID);
```
***
## Sending Game Invites
**Testing invites requires a second account.** A user cannot see their own Rich Presence Invite — you won't receive invites you send to yourself. To test the full invite flow, you'll need to use a separate test account.
Game invites can be sent in two ways:
1. Users can send game invites directly through the Discord client.
2. You can programmatically send game invites on a user's behalf through the SDK.
### Sending Game Invites in the Discord Client
Users can send game invites directly through the Discord client. This feature is described in detail in the [Game Invites help center article](https://support.discord.com/hc/en-us/articles/115001557452-Game-Invites).
### Sending Game Invites in the SDK
If a player has the required party, join secret, and supported platforms set in their rich presence, your game can send game invites programmatically through the SDK using [`Client::SendActivityInvite`].
[`Client::SendActivityInvite`] only works if Rich Presence is active with proper configuration
**Invite messages [require communication scopes](#prerequisites).**
The invite and join flow itself is powered by Rich Presence and
works with only the [default presence scopes](/developers/discord-social-sdk/core-concepts/oauth2-scopes#default-presence-scopes).
However, the invite **message** (e.g. `"Join my game!"`) is a Discord message.
If the sender is authorized with only presence scopes, the invite still sends and can be accepted, but its message
content will be empty.
```cpp theme={"system"}
uint64_t targetUserId = 1111785262289277050;
std::string inviteMessage = "Join my game!";
client->SendActivityInvite(targetUserId, inviteMessage, [](discordpp::ClientResult result) {
if(result.Successful()) {
std::cout << "Activity Invite sent to user" << std::endl;
} else {
std::cerr << "Failed - check if Rich Presence has party, secret, and platforms set" << std::endl;
}
});
```
***
## Receiving Game Invites
Game invites can also be received in two ways:
1. Users can receive game invites directly through the Discord client.
2. Your game can receive game invites for a user programmatically through the SDK.
### Receiving Game Invites in the Discord Client
Users can receive game invites directly in their DMs. This feature is described in detail in the [Game Invites help center article](https://support.discord.com/hc/en-us/articles/115001557452-Game-Invites).
### Receiving Game Invites in the SDK
Use [`Client::SetActivityInviteCreatedCallback`] to detect new invites and [`Client::AcceptActivityInvite`] to accept them. The callback you specify for [`Client::AcceptActivityInvite`] will be invoked with the join secret you set in Rich Presence.
```cpp theme={"system"}
client->SetActivityInviteCreatedCallback([&client](discordpp::ActivityInvite invite) {
std::cout << "Activity Invite received from user: " << invite.SenderId() << std::endl;
if(auto message = client->GetMessageHandle(invite.MessageId())){
std::cout << "Invite Message: " << message->Content() << std::endl;
}
client->AcceptActivityInvite(invite, [](discordpp::ClientResult result, std::string joinSecret) {
if(result.Successful()) {
std::cout << "Activity Invite accepted successfully!\n";
// joinSecret comes from the sender's Rich Presence configuration
// Use the joinSecret to connect the two players in your game
} else {
std::cerr << "❌ Activity Invite accept failed";
}
});
});
```
***
## Accepting Game Invites
Use [`Client::SetActivityJoinCallback`] to monitor for a user accepting a game invite, either in-game or in Discord. Use the join secret to connect the players in your game.
```cpp theme={"system"}
// This fires when a user clicks "Join" on someone's Rich Presence
client->SetActivityJoinCallback([&client](std::string joinSecret) {
// joinSecret is pulled from the host's Rich Presence ActivitySecrets
// Use the joinSecret to connect the players in your game
});
```
***
## Using Game Invites with Lobbies
Game invites can be used in conjunction with [Lobbies](/developers/discord-social-sdk/development-guides/managing-lobbies) to create a seamless experience for players to join a game session or party.
When a player accepts a game invite, you can use the join secret to connect the two players in your game.
An example flow might look like this:
* When a user starts playing the game, they create a lobby with a random secret string, using [`Client::CreateOrJoinLobby`]
* That user publishes their Rich Presence with the join secret set to the lobby secret, along with party size information
* Another user can then see that Rich Presence on Discord and request to join
* Once accepted, the new user receives the join secret, and their client can call CreateOrJoinLobby(joinSecret) to join the lobby
* Finally, the original user can notice that the lobby membership has changed, so they publish a new Rich Presence update containing the updated party size information
These examples use client-side lobby management but can also be adapted for lobbies created on the server side.
### Game Invite with Lobby Example
Here's a code example of how you might implement this flow:
```cpp theme={"system"}
// User A
// 1. Create a lobby with secret
std::string lobbySecret = "foo"
uint64_t USER_B_ID = 01234567890;
client->CreateOrJoinLobby(lobbySecret, [&client](discordpp::ClientResult result, uint64_t lobbyId) {
// 2. Update Rich Presence with a party and join secret to enable invites
discordpp::Activity activity{};
activity.SetType(discordpp::ActivityTypes::Playing);
activity.SetState("In Lobby");
// Rich Presence party configuration for how many players can join
discordpp::ActivityParty party{};
party.SetId("party1234");
party.SetCurrentSize(1);
party.SetMaxSize(4);
activity.SetParty(party);
// Rich Presence secret is what is shared via invites
discordpp::ActivitySecrets secrets{};
secrets.SetJoin(lobbySecret); // This connects Rich Presence to your lobby
activity.SetSecrets(secrets);
// Don't forget to set this Rich Presence update, otherwise SendActivityInvite won't work!
client->UpdateRichPresence(std::move(activity), [&client](discordpp::ClientResult result) {
// 3. NOW we can send invites because Rich Presence is configured
client->SendActivityInvite(USER_B_ID, "come play with me", [](discordpp::ClientResult result) {
if(result.Successful()) {
std::cout << "💌 Invite sent successfully!\n";
} else {
std::cerr << "❌ Invite failed - check Rich Presence configuration\n";
}
});
});
});
// User B
// 4. Monitor for new invites
client->SetActivityInviteCreatedCallback([&client](discordpp::ActivityInvite invite) {
std::cout << "💌 New invite received: " << invite.SenderId() << "\n";
// 5. When an invite is received, ask the user if they want to accept it.
// If they choose to do so then go ahead and invoke AcceptActivityInvite
client->AcceptActivityInvite(invite, [&client](discordpp::ClientResult result, std::string joinSecret) {
if (result.Successful()) {
std::cout << "🎮 Invite accepted! Joining lobby...\n";
// joinSecret came from User A's Rich Presence configuration
// 6. Join the lobby using the joinSecret
client->CreateOrJoinLobby(joinSecret, [=](discordpp::ClientResult result, uint64_t lobbyId) {
// Successfully joined lobby!
if (result.Successful()) {
std::cout << "🎮 Lobby joined successfully! " << lobbyId << std::endl;
} else {
std::cerr << "❌ Lobby join failed\n";
}
});
}
});
});
```
### Lobby Join Request Example
Users can also request to join each other's parties. This code example shows how that flow might look:
```cpp theme={"system"}
// User A
// 1. Create a lobby with secret
std::string lobbySecret = "foo";
uint64_t USER_A_ID = 286438705638408203;
client->CreateOrJoinLobby(lobbySecret, [&client](discordpp::ClientResult result, uint64_t lobbyId) {
// 2. Update Rich Presence with a party and join secret to enable invites
discordpp::Activity activity{};
activity.SetType(discordpp::ActivityTypes::Playing);
activity.SetState("In Lobby");
// Rich Presence party configuration for how many players can join
discordpp::ActivityParty party{};
party.SetId("party1234");
party.SetCurrentSize(1);
party.SetMaxSize(4);
activity.SetParty(party);
// Rich Presence secret is what is shared via invites
discordpp::ActivitySecrets secrets{};
secrets.SetJoin(lobbySecret);
activity.SetSecrets(secrets);
// This Rich Presence update is what enables the "Ask to Join" button in Discord
client->UpdateRichPresence(std::move(activity), [&client](discordpp::ClientResult result) {});
});
// User B
// 3. Request to join User A's party
client->SendActivityJoinRequest(USER_A_ID, [](discordpp::ClientResult result) {});
// User A
// 4. Monitor for new invites:
client->SetActivityInviteCreatedCallback([&client](discordpp::ActivityInvite invite) {
// 5. The game can now show that User A has received a request to join their party
// If User A is ok with that, they can reply back:
// Note: invite.type will be ActivityActionTypes::JoinRequest in this example
client->SendActivityJoinRequestReply(invite, [](discordpp::ClientResult result) {});
});
// User B
// 6. Same as before, user B can monitor for invites
client->SetActivityInviteCreatedCallback([&client](discordpp::ActivityInvite invite) {
std::cout << "💌 New invite received: " << invite.SenderId() << "\n";
// 7. When an invite is received, ask the user if they want to accept it.
// If they choose to do so then go ahead and invoke AcceptActivityInvite
client->AcceptActivityInvite(invite, [&client](discordpp::ClientResult result, std::string joinSecret) {
if (result.Successful()) {
std::cout << "🎮 Invite accepted! Joining lobby...\n";
// joinSecret came from User A's Rich Presence
// 5. Join the lobby using the joinSecret
client->CreateOrJoinLobby(joinSecret, [=](discordpp::ClientResult result, uint64_t lobbyId) {
// Successfully joined lobby!
if (result.Successful()) {
std::cout << "🎮 Lobby joined successfully! " << lobbyId << std::endl;
} else {
std::cerr << "❌ Lobby join failed\n";
}
});
}
});
});
```
***
## Supporting Mobile Game Invites
When a player receives a game invite on mobile, Discord must know how to launch your game. Game launching is handled through deep linking, which allows Discord to pass the join information to your game.
### Setting Up Mobile Deep Links
1. Configure your deep link URL in the Discord Developer Portal:
* Go to your application's `General` tab
* Enter your game's URL scheme. Any `https://` URL will work here but for maximum flexibility should be a [Universal Link for iOS](https://developer.apple.com/documentation/xcode/supporting-universal-links-in-your-app) or an [Android App Link](https://developer.android.com/training/app-links).
* Discord will append `/_discord/join?secret=SECRETHERE` to your URL
2. Tell Discord which platforms can accept invites:
```cpp theme={"system"}
activity.SetSupportedPlatforms(
ActivityGamePlatforms.Desktop | // Enable PC/Mac invites
ActivityGamePlatforms.IOS | // Enable iOS invites
ActivityGamePlatforms.Android // Enable Android invites
);
```
### How Mobile Deep Links Work
1. The user receives and accepts an invite in Discord
2. Discord launches your game using your URL scheme:
```
https://your-universal-app-link.com/_discord/join?secret=the_join_secret_you_set
```
3. Your game receives the URL and extracts the join secret
4. Use the secret to connect the player to the session
***
## Setting a Cover Image for the Invite (Optional)
You can set a cover image for an invite via rich presence activity. This image displays as the banner on the invite and helps draw attention to it. Setting a cover image via rich presence allows you to dynamically customize invites per rich presence activity; for example, showing different images based on game mode, map, etc. rather than using the same static image for all invites. If not set, the banner falls back to the "Rich Presence Invite Image" configured in the Developer Portal under the "Rich Presence" tab. Setting an invite cover image is entirely optional.
```cpp theme={"system"}
discordpp::ActivityAssets assets;
assets.SetInviteCoverImage("invite-cover-image"); // This example uses an asset key, but you can use an external URL asset for more dynamic cover images.
activity.SetAssets(assets);
```
***
## Next Steps
}>
Combine Discord and game friends into a single list for easy management.
}>
Bring players together in a shared lobby with invites, text chat, and voice comms.
}>
Enable private messaging between players.
Need help? Join the [Discord Developers Server](https://discord.gg/discord-developers) and share questions in the `#social-sdk-dev-help` channel for support from the community.
If you encounter a bug while working with the Social SDK, please report it here: [https://dis.gd/social-sdk-bug-report](https://dis.gd/social-sdk-bug-report)
***
## Change Log
| Date | Changes |
| -------------- | --------------------------------------------------------------------- |
| June 10, 2026 | Clarify that launch command registration is local to each machine |
| June 8, 2026 | Recommend communication scopes; clarify empty invite message behavior |
| March 17, 2025 | initial release |
[`Activity`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#ae793d9adbe16fef402b859ba02bee682
[`Client::AcceptActivityInvite`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ad12cf35065e4d2b303ee470af7c6ef37
[`Client::CreateOrJoinLobby`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a8b4e195555ecaa89ccdfc0acd28d3512
[`Client::GetDefaultCommunicationScopes`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a71499da752fbdc2d4326ae0fd36c0dd1
[`Client::RegisterLaunchCommand`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a024d7222931fdcb7d09c2b107642ecab
[`Client::RegisterLaunchSteamApplication`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a45b2c791c5b06f77d457dacb53dfba40
[`Client::SendActivityInvite`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#afc14e98fc070399895739da6d53efa60
[`Client::SetActivityInviteCreatedCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a3b4e37a222a8662506d763514774bedc
[`Client::SetActivityJoinCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a587d1c6d0352eba397c888987aa58418
# Creating and Managing Lobbies
Source: https://docs.discord.com/developers/discord-social-sdk/development-guides/managing-lobbies
Create and manage game lobbies with Discord Social SDK for matchmaking.
This feature is currently available with rate limits.
To increase the rate limits for your game, please follow
[Communication Features: Applying for Increased Rate Limits for Production Releases](/developers/discord-social-sdk/core-concepts/communication-features#applying-for-increased-rate-limits-for-production-releases).
## Overview
Lobbies are groups of users that can communicate via text and voice.
This guide will show you how to:
* Create and manage lobbies
* Handle lobby membership
* Send messages to lobbies
### Prerequisites
Before you begin, make sure you have:
* Set up the Discord Social SDK with our [Getting Started guide](/developers/discord-social-sdk/getting-started)
To utilize this communication feature, you must enable [`Client::GetDefaultCommunicationScopes`] in your OAuth Scope configuration.
See the [OAuth Scopes Core Concepts Guide](/developers/discord-social-sdk/core-concepts/oauth2-scopes) for more details.
[`Client::GetDefaultCommunicationScopes`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a71499da752fbdc2d4326ae0fd36c0dd1
***
## Lobby Features
Lobbies are groups of users that can communicate with each other via text and voice. Users can be in multiple lobbies at once. A lobby can also have metadata (an arbitrary JSON blob) associated with the lobby and each user.
### Lobby Members
Lobbies may have a maximum of 1,000 members, and each user may have a maximum of 100 lobbies per game. If your game needs more than 1,000 members in a lobby, please get in touch with us to discuss your use case.
### Text Chat
Lobbies have a text chat channel that all members can use to communicate. Messages are sent to all members of the lobby.
### Voice Chat
Lobbies support voice calls. Although a lobby is allowed to have 1,000 members, you should not start voice calls in lobbies that large.
We recommend sticking to around 25 members or fewer for voice calls, but please do contact Developer Support if you need more than 25 member calls so we can discuss your use case.
***
## Managing Lobbies
There are two ways to manage lobbies:
1. From your server using the Discord API.
2. From the client using the [`Client::CreateOrJoinLobby`] function.
### Server-Side Lobby Management
You can use the Discord HTTP API to create, update, and delete lobbies and manage lobby membership.
See the [Lobby](/developers/resources/lobby) API resource for available endpoints and [Use with Discord APIs](/developers/discord-social-sdk/how-to/use-with-discord-apis) for information on how to authenticate your requests.
Clients will not be able to use [`Client::CreateOrJoinLobby`], [`Client::CreateOrJoinLobbyWithMetadata`], or [`Client::LeaveLobby`] with lobbies created using the API.
### Client-Side Lobby Management
The SDK client can also create and join lobbies. This works by associating a secret value with the lobby. You can distribute this secret as necessary to folks, and they can then join the lobby using that secret. If the lobby does not exist, it will be created on demand.
* The relevant SDK functions are [`Client::CreateOrJoinLobby`] and [`Client::LeaveLobby`].
* Lobby secrets are unique per game (ie: application). For example, use a new secret if you want to generate a new lobby at the end of the match. Calling [`Client::LeaveLobby`] and then [`Client::CreateOrJoinLobby`] with the same secret value will just re-add you to the same lobby.
* Calling [`Client::CreateOrJoinLobby`] while the user is already in the lobby will update their metadata (if included) instead.
* Discord's Rich Presence system supports syncing this secret, too. Using this flow, clients can request to join another user's activity. When approved, the SDK will be given the secret, which you can access and join the associated lobby if you choose to do so. See the [Game Invites for Lobbies](/developers/discord-social-sdk/development-guides/managing-lobbies#creating-lobby-invites) example below.
* If you want to keep track of metadata for the lobby or the lobby members, you can use the [`Client::CreateOrJoinLobbyWithMetadata`] function. This function takes a JSON object as an argument, which will be stored with the lobby and the lobby members. This metadata can be retrieved using the discordpp::Client::GetLobbyHandle function.
```cpp theme={"system"}
// Create or join a lobby from the client
client->CreateOrJoinLobby("your-unique-lobby-secret",[client](discordpp::ClientResult result, uint64_t lobbyId) {
if(result.Successful()) {
std::cout << "🎮 Lobby created or joined successfully! Lobby Id: " << lobbyId << std::endl;
} else {
std::cerr << "❌ Lobby creation/join failed\n";
}
});
```
#### Leaving a Lobby
To remove a user from a lobby, use the [`Client::LeaveLobby`] function. Only lobbies created with [`Client::CreateOrJoinLobby`] can be left using [`Client::LeaveLobby`].
```cpp theme={"system"}
uint64_t lobbyId = 01234567890;
// Leaving a lobby from the client
client->LeaveLobby(lobbyId, [&](discordpp::ClientResult result) {
if(result.Successful()) {
std::cout << "🎮 Left lobby successfully! Lobby Id: " << lobbyId << std::endl;
} else {
std::cerr << "❌ Leaving lobby failed\n";
}
}
```
### Lobby Lifecycle
Lobbies are intended to be ephemeral and should be cleaned up when the game/match/area/world is no longer needed. To support this, you can set a "max idle time". This means that if a lobby sits idle, with no one connected to it at all, for more than that time, we will automatically delete the lobby. But as long as one person is connected, the lobby won't be deleted (and the timer resets too).
This value defaults to 5 minutes, and we expect that for most use cases, that will be ok. But we understand there are use cases such as "world chat" that might want to exist longer. The maximum value for this "idle time" will likely be 7 days, so that would mean that the lobby only gets deleted if no one connects to it for an entire week. This should give a good amount of permanence to lobbies when needed, but there may be rare cases where a lobby does need to be "rebuilt" if everyone is offline for an extended period.
Lobbies created by the SDK client using the [`Client::CreateOrJoinLobby`] function currently have an additional limitation. The "secret" value used in this call expires after 30 days, so the lobby will still exist, but new users won't be able to join the lobby after that.
***
## Sending Messages to a Lobby
Once you have a lobby created, lobby members can send messages to it using the [`Client::SendLobbyMessage`] function.
Clients can send messages to lobbies they are members of regardless of whether they joined the lobby using the client or server-side method.
```cpp theme={"system"}
uint64_t lobbyId = 01234567890;
client->SendLobbyMessage(lobbyId, "Hello", [](discordpp::ClientResult result, uint64_t messageId) {
if(result.Successful()) {
std::cout << "📨 Message sent successfully!\n";
} else {
std::cerr << "❌ Message sending failed\n";
}
});
```
***
## Receiving Lobby Messages
You can receive messages sent to a lobby using the [`Client::SetMessageCreatedCallback`] function.
This callback will fire whenever a new message is received in a lobby or a DM. From the `messageId`, you can fetch the [`MessageHandle`] and then the [`ChannelHandle`] to determine the location to which the message was sent.
```cpp theme={"system"}
client->SetMessageCreatedCallback([&client](uint64_t messageId) {
discordpp::MessageHandle message = client->GetMessageHandle(messageId);
std::cout << "📨 New message received: " << message->Content() << "\n";
});
```
***
## Getting Lobby Chat History
You can retrieve previous messages from a lobby using the [`Client::GetLobbyMessagesWithLimit`] function. This allows you to fetch chat history for display when users join a lobby or need to see previous conversations.
The function takes a lobby ID and a limit parameter to specify how many recent messages to retrieve.
**Important limitations:**
* Only a maximum of 200 messages and up to 72 hours of history can be retrieved
* Only messages from lobbies the user is currently a member of can be retrieved
```cpp theme={"system"}
const uint64_t lobbyId = 01234567890;
const uint32_t messageLimit = 50; // Number of recent messages to retrieve (max 200)
client->GetLobbyMessagesWithLimit(
lobbyId, messageLimit,
[](const discordpp::ClientResult &result, const std::vector &messages) {
if (result.Successful()) {
std::cout << "? Retrieved " << messages.size()
<< " messages from lobby chat history!\n";
// Process the messages (they are returned in chronological order)
for (const auto &message : messages) {
std::cout << "Message: " << message.Content() << std::endl;
}
} else {
std::cerr << "? Failed to retrieve lobby chat history\n";
}
});
```
The messages are returned as a list of [`MessageHandle`] objects, ordered chronologically from oldest to newest.
Each [`MessageHandle`] contains the message content, author information, and timestamp.
This is particularly useful for:
* Displaying recent chat when a user joins a lobby
* Implementing chat history scrollback features
* Preserving conversation context across game sessions
***
## Linking a Channel to Lobby
You can connect a lobby to a Discord text channel with Linked Channels. Linked Channels allows users to chat with each other in the lobby using Discord, even if they are not in the game.
See our guide on [Linked Channels](/developers/discord-social-sdk/development-guides/linked-channels) for more information on how to link a channel to a lobby.
***
## Managing Voice Chat
See our guide on [Managing Voice Chat](/developers/discord-social-sdk/development-guides/managing-voice-chat) for more information on how to start a voice call in a lobby.
***
## Creating Lobby Invites
Your game can use lobbies and game invites to allow users to invite friends to join an existing lobby.
Check out the [Using Game Invites with Lobbies](/developers/discord-social-sdk/development-guides/managing-game-invites#using-game-invites-with-lobbies) example to try it out in your game.
***
## Development Rate Limits
Applications without [increased rate limits for production releases](/developers/discord-social-sdk/core-concepts/communication-features#applying-for-increased-rate-limits-for-production-releases) are subject to the following application-wide limits:
| Operation | Limit |
| ---------------------------------- | --------------- |
| [`Client::CreateOrJoinLobby`] | 100 per 2 hours |
| [`Client::LinkChannelToLobby`] | 20 per 2 hours |
| [`Client::UnlinkChannelFromLobby`] | 20 per 2 hours |
| [`Client::SendLobbyMessage`] | 100 per 2 hours |
These are per-application rate limits, not per-user.
These limitations are designed to provide sufficient capacity for development, testing, and small-scale demos while ensuring system stability.
For guidance on detecting and retrying rate limited requests in the SDK, see [Handle Rate Limits](/developers/discord-social-sdk/how-to/handle-rate-limits).
***
## Next Steps
With your game able to create and manage lobbies, you can now implement additional features:
}>
Add voice communication to your lobbies.
}>
Connect lobbies to Discord text channels.
}>
Locally mute players in lobby voice calls based on block relationships.
Need help? Join the [Discord Developers Server](https://discord.gg/discord-developers) and share questions in the `#social-sdk-dev-help` channel for support from the community.
If you encounter a bug while working with the Social SDK, please report it here: [https://dis.gd/social-sdk-bug-report](https://dis.gd/social-sdk-bug-report)
***
## Change Log
| Date | Changes |
| -------------- | -------------------------------- |
| June 30, 2025 | Add communications scope warning |
| March 17, 2025 | initial release |
[`ChannelHandle`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ChannelHandle.html#ac32096b2ef15c5c220e9b7b92253cc46
[`Client::CreateOrJoinLobby`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a8b4e195555ecaa89ccdfc0acd28d3512
[`Client::CreateOrJoinLobbyWithMetadata`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a5c84fa76c73cf3c0bfd68794ca5595c1
[`Client::GetLobbyMessagesWithLimit`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a0586192e85caf548b8b321f1cb21301f
[`Client::LeaveLobby`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a8c78f797240b35d721383461a2e62926
[`Client::LinkChannelToLobby`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a3114d58d50d4d2cb5752d95e121315d4
[`Client::SendLobbyMessage`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a779e0483f51dc99f0db3dd761d22ab6f
[`Client::SetMessageCreatedCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a28325a8e8c688a84ac851da4bc86e148
[`Client::UnlinkChannelFromLobby`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a28f78a6fe46eb11eb54ee9b53fa94ffe
[`MessageHandle`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#ae25595b43bc74b0c4c92c5165d16382f
# Managing Relationships in Your Game
Source: https://docs.discord.com/developers/discord-social-sdk/development-guides/managing-relationships
Manage Discord user relationships including friends and blocked users in your application.
## Overview
The Discord Social SDK lets you manage relationships between players in your game. This guide will show you how to:
* Send and accept friend requests
* Handle different types of relationships
* Block and unblock users
* Work with both Discord-wide and game-specific friendships
### Prerequisites
Before you begin, make sure you have:
* Completed the [Getting Started](/developers/discord-social-sdk/getting-started) guide
* Completed the [Creating a Unified Friends List](/developers/discord-social-sdk/development-guides/creating-a-unified-friends-list) guide
This feature requires the **Default Presence Scopes** (`openid` and `sdk.social_layer_presence`).
Use [`Client::GetDefaultPresenceScopes`] when configuring your OAuth2 flow.
See the [OAuth2 Scopes guide](/developers/discord-social-sdk/core-concepts/oauth2-scopes) for details on all available scopes.
[`Client::GetDefaultPresenceScopes`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a7648bd1d2f7d9a86ebd0edb8bef12b5c
***
## Understanding Relationship Types
Discord models the relationship between two users using the Relationship entity in the SDK. Relationships are not just for friends. They are also used to send and receive friend requests and block other users.
While the SDK allows you to manage a user's relationships, you should never act without their explicit consent. You should not automatically send or accept friend requests. Only invoke APIs to manage relationships in response to a user action such as clicking a "Send Friend Request" button.
### Relationship Types
We know that sometimes users will want to be friends with each other across all their games. If they start playing a new game, they can see all of their previous friends and don't start from scratch. But sometimes, they don't want to give out that access and only want to be friends in the current game they are playing.
To support this, the Discord Social SDK supports two types of relationships between users:
* **Discord relationships**: These relationships persist across games and on the Discord client. Both users can see whether each other is online, regardless of whether they are in the same game. Discord Relationships are the same as becoming a friend in the Discord client.
* **Game relationships**: These are per-game relationships and do not carry over to other games. The two users can only see if the other is online if they are playing a game in which they are friends. Game friends can DM each other, and those DMs will show up in Discord, but they can disable that behavior and keep their game conversations restricted to just the game. That option is located in the "Content & Social" User settings, under the "Connected Games" tab at the top of the page.
[`RelationshipHandle`] can be used to determine the type of friendship between the player and another user. It has two fields:
* [`RelationshipHandle::DiscordRelationshipType`] for the **Discord friendship**
* [`RelationshipHandle::GameRelationshipType`] for the **game friendship**
Having both of these friend types is important because a pair of users might start out as game friends but later choose to "upgrade" to being full Discord friends. In this case, their [`RelationshipHandle::DiscordRelationshipType`] would be set to `RelationshipType::PendingIncoming` or `RelationshipType::PendingOutgoing` (based on whether they are receiving or sending the request respectively), and their [`RelationshipHandle::GameRelationshipType`] would remain as `RelationshipType::Friend`.
While our API technically supports users being both types of friends, you don't have to ensure that every Discord friend is a game friend or vice versa. When adding friends, offer users a choice of friend type and explain the difference. See our design guidelines for more.
### Discord Friend Relationships
* Persist across all games and Discord
* Limited to 1,000 friends
* Online status visible everywhere
* Full Discord chat functionality
### Game Friend Relationships
* Only exist within your game
* No current friend limit
* Online status is only visible in-game
***
## Relationship Actions
Once you've [created a unified friends list](/developers/discord-social-sdk/development-guides/creating-a-unified-friends-list), you can start managing relationships between players in your game.
Here are some common actions you might want to take:
### Sending Game Friend Requests
Sends (or accepts) a game friend request to the target user.
You can send game friend requests to users using their Discord unique username or user ID.
After the friend request is sent, each user will have a new game relationship created. For the current user, the [`RelationshipHandle::GameRelationshipType`] will be `RelationshipType::PendingOutgoing`, and for the target user, it will be `RelationshipType::PendingIncoming`.
If the current user has already received a game friend request from the target user (meaning [`RelationshipHandle::GameRelationshipType`] is `RelationshipType::PendingIncoming`), the two users will become game friends.
```cpp theme={"system"}
client->SendGameFriendRequest("username", [](discordpp::ClientResult result) {
if (result.Successful()) {
std::cout << "🎮 Game friend request sent successfully!\n";
}
});
client->SendGameFriendRequestById(123456789, [](discordpp::ClientResult result) {
if (result.Successful()) {
std::cout << "🎮 Game friend request sent successfully!\n";
}
});
```
### Sending Discord Friend Requests
Sends (or accepts) a Discord friend request to the target user.
You can send Discord friend requests to users by using their Discord unique username or user ID.
After the friend request is sent, each user will have a new Discord relationship created. For the current user, the [`RelationshipHandle::DiscordRelationshipType`] will be `RelationshipType::PendingOutgoing`, and for the target user, it will be `RelationshipType::PendingIncoming`.
If the current user has already received a Discord friend request from the target user (meaning [`RelationshipHandle::DiscordRelationshipType`] is `RelationshipType::PendingIncoming`), the two users will become Discord friends.
```cpp theme={"system"}
client->SendDiscordFriendRequest("username", [](discordpp::ClientResult result) {
if (result.Successful()) {
std::cout << "🎮 Discord friend request sent successfully!\n";
}
});
client->SendDiscordFriendRequestById(123456789, [](discordpp::ClientResult result) {
if (result.Successful()) {
std::cout << "🎮 Discord friend request sent successfully!\n";
}
});
```
### Accept Incoming Friend Requests
Allow your players to accept incoming friend requests, which `RelationshipType::PendingIncoming` represents.
```cpp theme={"system"}
// RelationshipHandle::DiscordRelationshipType == RelationshipType::PendingIncoming
client->AcceptDiscordFriendRequest(userId, [](discordpp::ClientResult result) {
if (result.Successful()) {
std::cout << "🎮 Discord friend request accepted!\n";
}
});
// RelationshipHandle::GameRelationshipType == RelationshipType::PendingIncoming
client->AcceptGameFriendRequest(userId, [](discordpp::ClientResult result) {
if (result.Successful()) {
std::cout << "🎮 Game friend request accepted!\n";
}
});
```
### Reject Incoming Friend Requests
Allow your players to reject incoming friend requests, which `RelationshipType::PendingIncoming` represents.
```cpp theme={"system"}
// Reject Incoming Friend Requests
// RelationshipHandle::DiscordRelationshipType == RelationshipType::PendingIncoming
client->RejectDiscordFriendRequest(userId,[](discordpp::ClientResult result) {
if (result.Successful()) {
std::cout << "🎮 Discord friend request rejected!\n";
}
});
// RelationshipHandle::GameRelationshipType == RelationshipType::PendingIncoming
client->RejectGameFriendRequest(userId, [](discordpp::ClientResult result) {
if (result.Successful()) {
std::cout << "🎮 Game friend request rejected!\n";
}
});
```
### Cancel Outgoing Friend Requests
Allow your players to cancel outgoing friend requests, which `RelationshipType::PendingOutgoing` represents.
```cpp theme={"system"}
// Cancel Outgoing Friend Requests
// RelationshipHandle::DiscordRelationshipType == RelationshipType::PendingIncoming
client->CancelDiscordFriendRequest(userId, [](discordpp::ClientResult result) {
if (result.Successful()) {
std::cout << "🎮 Discord friend request canceled!\n";
}
});
// RelationshipHandle::GameRelationshipType == RelationshipType::PendingOutgoing
client->CancelGameFriendRequest(userId, [](discordpp::ClientResult result) {
if (result.Successful()) {
std::cout << "🎮 Game friend request canceled!\n";
}
});
```
### Managing Existing Relationships
Allow your players to remove existing relationships with other users. This will remove the relationship from both users, and they will no longer be able to see each other's online status or send messages.
```cpp theme={"system"}
// Removes any friendship between the current user and the target user.
// This function will remove BOTH any Discord friendship and any game friendship between the users.
client->RemoveDiscordAndGameFriend(userId, [](discordpp::ClientResult result) {
if (result.Successful()) {
std::cout << "🎮 Discord and Game friendships removed!\n";
}
});
// Removes any game friendship between the current user and the target user.
client->RemoveGameFriend(userId, [](discordpp::ClientResult result) {
if (result.Successful()) {
std::cout << "🎮 Game friendship removed!\n";
}
});
```
### Blocking Users
Allow your players to block another user so they cannot send friend or activity invites and cannot message them anymore.
Blocking a user will also remove any existing relationship between the two users and persist across games, so blocking a user in one game or on Discord will block them in all other games and on Discord.
```cpp theme={"system"}
client->BlockUser(userId, [](discordpp::ClientResult result) {
if (result.Successful()) {
std::cout << "🎮 User blocked successfully!\n";
}
});
```
### Unblocking Users
Allow your players to unblock another user if they have been blocked. Unblocking a user does not restore any previous relationships between the users.
```cpp theme={"system"}
client->UnblockUser(userId, [](discordpp::ClientResult result) {
if (result.Successful()) {
std::cout << "🎮 User unblocked successfully!\n";
}
});
```
***
## Next Steps
Continue learning about the Discord Social SDK with these guides:
}>
Combine Discord and game friends into a single list for easy management.
}>
Allow players to invite friends to join their game session or party.
}>
Bring players together in a shared lobby with invites, text chat, and voice comms.
Need help? Join the [Discord Developers Server](https://discord.gg/discord-developers) and share questions in the `#social-sdk-dev-help` channel for support from the community.
If you encounter a bug while working with the Social SDK, please report it here: [https://dis.gd/social-sdk-bug-report](https://dis.gd/social-sdk-bug-report)
***
## Change Log
| Date | Changes |
| -------------- | --------------- |
| March 17, 2025 | Initial release |
[`RelationshipHandle`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1RelationshipHandle.html#a7da36b15ad0b7d38ba658a622e9ded77
[`RelationshipHandle::DiscordRelationshipType`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1RelationshipHandle.html#a5fecfb79a4a2b6f3dc5f73b09d0c3881
[`RelationshipHandle::GameRelationshipType`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1RelationshipHandle.html#aa60146eb72ede07e3e615565f61f97eb
# Managing Voice Chat
Source: https://docs.discord.com/developers/discord-social-sdk/development-guides/managing-voice-chat
Integrate Discord voice chat features into your application.
This feature is currently available with rate limits.
To increase the rate limits for your game, please follow
[Communication Features: Applying for Increased Rate Limits for Production Releases](/developers/discord-social-sdk/core-concepts/communication-features#applying-for-increased-rate-limits-for-production-releases).
## Overview
Voice calls are a core feature of the Discord Social SDK that enable real-time voice communication between players in your game within lobbies.
This guide will show you how to:
* Start and join voice calls in lobbies
* Control voice settings like mute, deafen, and volume
* Process audio data with custom callbacks
* Integrate with external audio systems
* Check voice call status and participant states
## Prerequisites
Before you can start a voice call, you must complete these essential steps:
To utilize this communication feature, you must enable [`Client::GetDefaultCommunicationScopes`] in your OAuth Scope configuration.
See the [OAuth Scopes Core Concepts Guide](/developers/discord-social-sdk/core-concepts/oauth2-scopes) for more details.
[`Client::GetDefaultCommunicationScopes`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a71499da752fbdc2d4326ae0fd36c0dd1
### 1. Lobby Management
**Voice calls require an active lobby with participants.** You must:
1. **Create or join a lobby** using the Discord Social SDK
2. **Add players to the lobby** - voice calls only work with lobby members
Essential Reading: For detailed instructions on creating lobbies, joining lobbies, and managing lobby members, see
the complete [Managing Lobbies Guide](/developers/discord-social-sdk/development-guides/managing-lobbies).
### 2. Lobby Size Limitations
While Discord lobbies technically support up to 1,000 members, **voice calls should be limited to much smaller groups**. We **strongly recommend keeping voice calls to 25 members or fewer** for optimal performance and user experience.
***
## Starting and Joining Voice Calls
The Discord Social SDK makes it simple to start and join voice calls - both operations use the same functions whether you're creating something new or joining something that already exists.
### Creating/Joining a Lobby and Starting/Joining a Call
Here's a complete example of joining a lobby and starting a voice call:
```cpp theme={"system"}
// First, create or join a lobby using a shared secret
const std::string lobbySecret = "my-game-lobby-secret";
client->CreateOrJoinLobby(lobbySecret, [client](const discordpp::ClientResult& result, uint64_t lobbyId) {
if (result.Successful()) {
std::cout << "🎮 Successfully joined lobby!" << std::endl;
// Now start or join the voice call in this lobby
// StartCall returns a Call object but has no callback
const auto call = client->StartCall(lobbyId);
// StartCall returns null if user is already in this voice channel
if (call) {
std::cout << "🎤 Voice call operation initiated..." << std::endl;
} else {
std::cout << "ℹ️ Already in this voice channel" << std::endl;
}
} else {
std::cerr << "❌ Failed to join lobby: " << result.Error() << std::endl;
}
});
```
### How It Works
Both [`Client::CreateOrJoinLobby`] and [`Client::StartCall`] are designed to handle existing and new scenarios automatically:
* [`Client::CreateOrJoinLobby`]: If a lobby with the given secret already exists, you'll join it. If not, a new
lobby is created with that secret.
* [`Client::StartCall`]: If a voice call is already active in the lobby, you'll join it. If not, a new voice call is
started.
You don't need to check if a lobby exists or if a call is already ongoing. The SDK handles both scenarios seamlessly, making your code simpler and more reliable.
## Controlling Voice Features
The Discord Social SDK provides comprehensive voice control options at both the individual call level and globally across all calls.
### Global Voice Controls
These methods control voice settings across all active calls using the [`Client`] object:
* [`Client::SetSelfMuteAll`] - Mutes your microphone across all active calls
* [`Client::SetSelfDeafAll`] - Deafens you across all active calls
* [`Client::SetInputVolume`] - Sets microphone volume
* [`Client::SetOutputVolume`] - Sets speaker volume
### Per-Call Voice Controls
These methods control voice settings for a specific call using the [`Call`] object:
* [`Call::SetSelfMute`] - Mutes your microphone so other participants in this call cannot hear you
* [`Call::SetSelfDeaf`] - Mutes all audio from this call so you cannot hear other participants, and they cannot hear you either
* [`Call::SetParticipantVolume`] - Adjusts the volume of a specific participant
### Voice Activity Detection
Use [`Call::SetVADThreshold`] to control voice activation detection sensitivity. This allows optional fine-tuning of
when the system considers someone to be speaking.
```cpp theme={"system"}
// Per-call controls (assuming you have a Call object)
uint64_t lobbyId = 123456789; // Your lobby ID
auto call = client->GetCall(lobbyId);
if (call) {
call.SetSelfMute(true); // Mute in this call only
call.SetSelfDeaf(false); // Unmute audio in this call
call.SetParticipantVolume(userId, 150.0f); // Increase participant volume
call.SetVADThreshold(false, -30.0f); // Set custom voice detection threshold
}
// Global controls
client->SetSelfMuteAll(true); // Mute across all calls
client->SetInputVolume(75.0f); // Set microphone to 75%
client->SetOutputVolume(120.0f); // Increase speaker volume to 120%
```
### Detecting No Audio Input
The SDK can notify you when no audio is reaching the microphone — for example, when a user's mic is broken, muted at the OS level, or the wrong input device is selected. This lets your game surface a "your mic appears silent" hint instead of leaving the user to wonder why nobody can hear them.
* [`Client::SetNoAudioInputThreshold`] — dBFS threshold for what counts as "no input." Range `[-100.0, 100.0]`, defaults to `-100.0` (detection disabled). Set to something like `-60.0` to enable.
* [`Client::SetNoAudioInputCallback`] — receives a `bool inputDetected` whenever the mic crosses the threshold (silent → active or active → silent).
```cpp theme={"system"}
// Enable detection at -60 dBFS
client->SetNoAudioInputThreshold(-60.0f);
client->SetNoAudioInputCallback([](bool inputDetected) {
if (!inputDetected) {
// Show a UI hint: "Your mic appears silent — check your device settings."
} else {
// Mic is receiving audio again; clear the hint.
}
});
```
## Noise Suppression & Cancellation
The SDK provides two tiers of microphone audio processing: a set of WebRTC-based defaults that are always on, and an optional Krisp-powered noise cancellation for higher-quality results.
Krisp delivers higher-quality noise cancellation, but ships extra libraries and model files that increase your installation size. If size is a constraint — for example on mobile — see [Excluding Krisp to Reduce Installation Size](#excluding-krisp-to-reduce-installation-size) for how to ship with only the WebRTC defaults.
### Default Audio Processing (WebRTC)
These three processors ship with every build and default to on. They use the WebRTC library's standard audio pipeline:
* [`Client::SetNoiseSuppression`] — suppresses steady background noise (e.g. fans, keyboards, room tone). Defaults
to on.
* [`Client::SetEchoCancellation`] — removes echo from speakers being picked up by the mic. Defaults to on.
* [`Client::SetAutomaticGainControl`] — automatically normalizes microphone volume for clarity and consistency. Defaults to on.
```cpp theme={"system"}
// Toggle individual WebRTC processors from a voice settings UI
client->SetNoiseSuppression(true);
client->SetEchoCancellation(true);
client->SetAutomaticGainControl(true);
```
### Advanced Noise Cancellation (Krisp)
[`Client::SetNoiseCancellation`] enables Krisp, a noise cancellation technology that removes a much wider range of
background sounds (e.g. typing, dogs barking, traffic) than the WebRTC suppression. It defaults to off.
Krisp and WebRTC noise suppression are mutually exclusive. Enabling [`Client::SetNoiseCancellation`] automatically disables [`Client::SetNoiseSuppression`] — you don't need to turn it off yourself.
```cpp theme={"system"}
// Enable Krisp noise cancellation
client->SetNoiseCancellation(true);
```
### Excluding Krisp to Reduce Installation Size
Krisp ships as additional libraries and model files alongside the core SDK, which adds to your installation size. If you're optimizing for size — for example on mobile — you can ship without Krisp and rely on the WebRTC noise suppression instead.
To exclude Krisp from your distribution, remove all `.kef` and `.kw` files along with any file or directory whose name
contains `krisp` (for example `discord_krisp.dll`, `libdiscord_krisp.dylib`, `discord_partner_sdk_krisp.aar`, and `discord_partner_sdk_krisp.xcframework`).
## Advanced Audio Processing
### Manipulating Voice Data with Callbacks
For advanced audio processing needs, use [`Client::StartCallWithAudioCallbacks`] to access raw audio data. This
enables real-time audio manipulation and integration with external audio processing systems.
### In-Place Audio Modification
To directly modify incoming audio samples (e.g., volume dampening):
```cpp theme={"system"}
const auto call = client->StartCallWithAudioCallbacks(
lobbyId,
[](uint64_t userId, int16_t *data, const size_t samplesPerChannel,
int sampleRate, const size_t channels,
bool &outShouldMuteData) {
// Dampen volume of incoming audio by modifying data's samples
// in-place
for (int i = 0; i < samplesPerChannel * channels; i++) {
data[i] *= 0.5; // Reduce volume by 50%
}
},
[](int16_t *data, uint64_t samplesPerChannel, int32_t sampleRate,
uint64_t channels) {});
```
### External Audio Pipeline Integration
To route audio to external processing systems such as [FMOD](https://www.fmod.com/) or
[Wwise](https://www.audiokinetic.com/en/wwise/overview/):
```cpp theme={"system"}
const auto call = client->StartCallWithAudioCallbacks(lobbyId,
[](uint64_t userId, int16_t* data, size_t samplesPerChannel,
int sampleRate, size_t channels, bool& outShouldMuteData) {
// Prevent Discord from playing the audio directly
outShouldMuteData = true;
const int totalNumSamples = samplesPerChannel * channels;
// Send audio data to your external audio system
SendAudioToExternalAudioSystem(data, totalNumSamples);
},
[](int16_t *data, uint64_t samplesPerChannel, int32_t sampleRate,
uint64_t channels) {});
```
### Key Audio Processing Points
1. **Direct Manipulation**: The `data` parameter in `Client::UserAudioReceivedCallback` can be modified in-place to alter incoming audio samples
2. **External Processing**: Set `outShouldMuteData = true` to prevent Discord from playing audio directly, allowing you to handle it through your own audio pipeline
3. **No Encoding Required**: The SDK handles all voice encoding/decoding automatically - you work with raw audio samples
## Ending Voice Calls
When you need to terminate voice calls, you have two options:
### End a Call For a Specific Lobby
```cpp theme={"system"}
uint64_t lobbyId = 123456789; // Your lobby ID
client->EndCall(lobbyId, []() {
std::cout << "🔇 Call ended successfully" << std::endl;
});
```
### End All Calls
```cpp theme={"system"}
client->EndCalls([]() {
std::cout << "🔇 All calls ended successfully" << std::endl;
});
```
## Checking Lobby Voice Call Status
You may want to check the voice call status for your lobby to display UI indicators, monitor participant activity, or provide information to players. The Discord Social SDK provides several ways to inspect active voice calls and participant states.
### Checking if a Call is Active
Use `LobbyHandle::GetCallInfoHandle()` to determine if there's an active voice call in your lobby:
```cpp theme={"system"}
// Check if there's an active call in the lobby
const auto callInfoHandle = lobby->GetCallInfoHandle();
if (callInfoHandle) {
// There's an active call - you can join it or get participant
// info
const auto participants = callInfoHandle->GetParticipants();
std::cout << "Active call with " << participants.size()
<< " participants" << std::endl;
} else {
// No active call in this lobby
std::cout << "No active voice call in this lobby" << std::endl;
}
```
### Checking Individual Participant Status
For each participant in a voice call, you can check their voice state using `VoiceStateHandle`:
```cpp theme={"system"}
// Get voice state information for participants
const auto callInfo = lobby->GetCallInfoHandle();
if (callInfo) {
const auto participants = callInfo->GetParticipants();
for (const auto &participantId : participants) {
const auto voiceState = callInfo->GetVoiceStateHandle(participantId);
if (voiceState) {
const bool isMuted = voiceState->SelfMute();
const bool isDeafened = voiceState->SelfDeaf();
std::cout << "Participant " << participantId
<< " - Muted: " << (isMuted ? "Yes" : "No")
<< ", Deafened: " << (isDeafened ? "Yes" : "No")
<< std::endl;
}
}
}
```
### Voice State Information Available
The [`VoiceStateHandle`] provides these key details about each participant:
* [`VoiceStateHandle::SelfMute`]: Returns `true` if the user has muted themselves (others cannot hear them)
* [`VoiceStateHandle::SelfDeaf`]: Returns `true` if the user has deafened themselves (they cannot hear others and others cannot hear them)
This information is particularly useful for:
* Displaying voice indicators in your UI
* Implementing voice-related features or debugging audio issues
***
## Diagnosing Audio Issues
If users report echo, feedback, or other audio quality problems, the SDK offers dedicated tooling for capturing voice and audio diagnostics. See the following sections of the Debug & Log guide:
* [Voice Logging](/developers/discord-social-sdk/how-to/debug-log#voice-logging) — capture logs from the voice subsystem and underlying WebRTC layer.
* [Audio Logging](/developers/discord-social-sdk/how-to/debug-log#audio-logging) — record input/output waveforms to disk for offline analysis.
***
## Next Steps
}>
Integrating and managing content moderation for your game when using the Discord Social SDK.
}>
Make requests to Discord's HTTP APIs from your game.
}>
Mute players in lobby voice calls based on block relationships.
Need help? Join the [Discord Developers Server](https://discord.gg/discord-developers) and share questions in the `#social-sdk-dev-help` channel for support from the community.
If you encounter a bug while working with the Social SDK, please report it here: [https://dis.gd/social-sdk-bug-report](https://dis.gd/social-sdk-bug-report)
***
## Change Log
| Date | Changes |
| -------------- | ----------------------------------------------------------------- |
| May 13, 2026 | Add noise suppression, cancellation, and no-audio-input detection |
| June 30, 2025 | Add communications scope warning |
| June 19, 2025 | released guide |
| March 17, 2025 | initial release |
[`Call`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#a1cc8a7f73c15a960bc409d734b5edbd1
[`Call::SetParticipantVolume`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#ad974fadbe89c453e4d8a3f9824e21ceb
[`Call::SetSelfDeaf`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#a07d67c210f2a4655c6f1d2899c6d32d6
[`Call::SetSelfMute`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#afa35a5d6a4564df97452df58bb74f617
[`Call::SetVADThreshold`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#a7c3fd83c5dfe37d796e30c5e28c93b6e
[`Client`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a91716140c699d8ef0bdf6bfd7ee0ae13
[`Client::CreateOrJoinLobby`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a8b4e195555ecaa89ccdfc0acd28d3512
[`Client::SetAutomaticGainControl`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a818ae7f46b5bd3873dcd51dd3d9fa64d
[`Client::SetEchoCancellation`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a1def244b7ecd388902ba5256ce506ca3
[`Client::SetInputVolume`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ad4358f5baffd9a5f2a6fa74d62459313
[`Client::SetNoAudioInputCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a479e60724bf6b0b39b555c1ff8489b9e
[`Client::SetNoAudioInputThreshold`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ab33f5d70461ee7590b6f3cfccaeb6df4
[`Client::SetNoiseCancellation`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a54aad09e8e06dc327695209b733d3f4c
[`Client::SetNoiseSuppression`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ae3f6e33b956964525adfa4536bd1fe73
[`Client::SetOutputVolume`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a61a9321a79479c8b1be1559e2bbdd934
[`Client::SetSelfDeafAll`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a59be56ae5752e9f2f0f299bc552282b2
[`Client::SetSelfMuteAll`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a9c6ef96590533d103a866cb8a99d2669
[`Client::StartCall`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aef4f25d761fe198fbe9bc721fc24d83f
[`Client::StartCallWithAudioCallbacks`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#abcaa891769f9e912bfa0e06ff7221b05
[`VoiceStateHandle`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1VoiceStateHandle.html#aad2d4454b6677d82721128b0cd98a2d8
[`VoiceStateHandle::SelfDeaf`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1VoiceStateHandle.html#a9fd4ac5fb813b926d1336fc65b440f42
[`VoiceStateHandle::SelfMute`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1VoiceStateHandle.html#a5476a6e8d5e9092a153b4646371a9f3f
# Publisher Level Account Linking
Source: https://docs.discord.com/developers/discord-social-sdk/development-guides/publisher-level-account-linking
Publisher Level Account Linking requires Discord approval.
To inquire about access, speak to your Discord account representative or [contact us](https://discord.com/developers/contact-us)
## Overview
Publisher Level Account Linking enables a developer with multiple games to have a single
authorization flow across all games in their portfolio. Players authenticate through a publisher application
instead of authenticating separately for each game.
This is done by establishing a parent-child application hierarchy between the publisher
application (parent), and game applications (children).
Once established, the publisher application can then perform player authentication, and its game
application can be used to connect and utilize the Discord Social SDK.
This guide will show you how to:
* Set up publisher and game applications
* Exchange parent tokens for child tokens
* Use child tokens with the Discord Social SDK
* Understand token lifecycle and limitations
## Benefits
### For Publishers
* **Centralized Account Management**: Manage user authorization at the publisher level
### For Players
* **Simplified Experience**: Reduced friction when playing multiple games from the same publisher
* **Richer Social Features**: Access enriched friend data and activity across the publisher's game catalog
***
## Is Publisher Level Account Linking Right for You?
Publisher Level Account Linking is designed for publishers with centralized, cross-game social ecosystems. Not all multi-game publishers are a good fit.
| **Good Fit** | **Poor Fit** |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- |
| Publishers with cohesive portfolios where games share a centralized account system **and** a publisher-level social experience (shared friends list across games). | Publishers with disparate game portfolios that share only a centralized login but have no cross-game social features. |
Publisher Level Account Linking was designed to support publishers with cross-game social ecosystems. When a player links their Discord account to a publisher account and that publisher shares a social graph across games, the authorization feels natural and expected — players already understand that their friends in one game appear across all games in the portfolio.
### Key Behaviors to Understand
Before committing to Publisher Level Account Linking, understand these architectural constraints:
#### Authorization is all-or-nothing at the publisher level:
* When a player links their Discord account to the publisher application, that authorization applies to **all** child game applications under the publisher
* Players cannot opt out of the Discord connection for individual games while remaining linked at the publisher level
* Revoking authorization disconnects the Discord account from the publisher account, affecting all games
#### Relationships exist at the publisher level, not per-game:
Game friend lists exist at the publisher level — there are no per-game sandboxed friend lists. If you need separate friend lists per game, Publisher Level Account Linking is not the right solution.
**This is a one-way architectural decision.** Once child applications are linked to a parent publisher application, [data is deleted and the relationship cannot be reverted](#3-establish-parent-child-relationships). Evaluate carefully before migrating existing applications.
***
## How It Works
The Publisher Authentication system uses two types of applications:
1. **Publisher application (parent)**: Executes OAuth2 account linking and manages user authorization
2. **Game application (child)**: Uses child access tokens obtained from the parent for Social SDK operations
The flow works as follows:
1. User authenticates once through the publisher application using standard OAuth2
2. Developer exchanges the parent access token for a child access token using the `/partner-sdk/child-token` endpoint
3. Child token is used to initialize the Discord Social SDK for the specific game
4. All Social SDK operations are now performed with the child token
We can view this as a table to see which responsibilities lie with the Publisher application and which lie with the
game application:
| **Action** | **Publisher Application (Parent)** | **Game Application (Child)** |
| ----------------------------------------------- | :--------------------------------: | :--------------------------: |
| Discord account authentication | ✅ | ❌ |
| Provisional account authentication | ✅ | ❌ |
| Issues parent access tokens with refresh tokens | ✅ | ❌ |
| Exchange parent tokens for child token | ✅ | ❌ |
| Used to connect to Social SDK | ❌ | ✅ |
| Operates in context of specific game | ❌ | ✅ |
***
## Prerequisites
Before you begin, make sure you have:
* A Discord application configured as the publisher (parent) with OAuth2 enabled
* One or more Discord applications configured as games (children)
* Social SDK integration enabled for all applications
* Contact with Discord to establish the parent-child relationships between applications
***
## Application Configuration
### 1. Configure the Publisher Application
Set up your publisher application following the standard [OAuth2 guidelines](/developers/discord-social-sdk/development-guides/account-linking-with-discord):
1. Navigate to the [Developer Portal](https://discord.com/developers/applications)
2. Select your publisher application
3. Configure OAuth2 redirect URIs on the OAuth2 settings page
4. Enable the Social SDK integration
5. Note your Client ID and Client Secret
### 2. Configure Game Applications
For each game application:
1. Create a new application in the [Developer Portal](https://discord.com/developers/applications)
2. Enable Social SDK integration
3. **Do not configure OAuth2 redirect URIs** - game applications don't need them
4. Note the application's Client ID
Game applications do not require OAuth2 configuration since they use child tokens obtained from the parent application.
### 3. Establish Parent-Child Relationships
You will need to contact Discord to link your game applications to your publisher application.
To link child game applications to the parent publisher application, provide Discord with the following:
* Parent application ID (publisher)
* Child application IDs (games)
Discord will configure the relationships in their systems to enable token exchange.
Publisher applications and game applications are restricted from certain actions once the parent-child relationship
is established.
If existing applications are being migrated to either publisher or game applications, some flows may be broken as
soon as the relationship is configured. We recommend discussing this with Discord before initiating the
migration.
Alternatively, create new applications for both publisher and game applications as part of implementing Publisher
Level Account Linking.
When you establish a link, the child application has the following data deleted:
| **Data Type** | **Action** |
| --------------------------- | ----------------------------------------------------- |
| OAuth2 tokens | All tokens for the child app are deleted |
| Lobbies | All lobbies associated with the child app are deleted |
| User Application Identities | Deleted for all users |
| Provisional accounts | All provisional users are deleted |
| Game Relationships | Removed for all users associated with the app |
The parent application is unaffected - existing tokens remain valid since they can still be used in normal OAuth2 flows.
### 4. Match Confidentiality Settings
Ensure that child applications match the confidentiality setting of the parent application:
* If the parent is a **confidential client** (has a client secret), all children must be confidential clients
* If the parent is a **public client**, all children must be public clients
***
## Implementing Token Exchange
Publisher Level Account Linking supports both provisional accounts for users without Discord accounts and standard
OAuth2 for users with linked Discord accounts.
### Linked Discord Account Flow
```mermaid theme={"system"}
sequenceDiagram
participant User
participant GameClient
participant PublisherServer
participant DiscordAPI
Note over User, DiscordAPI: Standard OAuth Flow for Parent Application
User->>DiscordAPI: Visit authorization URL
DiscordAPI->>User: Present authorization prompt
User->>DiscordAPI: Grant permission
DiscordAPI->>User: Redirect with authorization code
User->>GameClient: Authorization code provided
GameClient->>PublisherServer: Forward authorization code
PublisherServer->>DiscordAPI: POST oauth2/token (code + client_secret)
DiscordAPI-->>PublisherServer: Parent access token & refresh token
Note over User, DiscordAPI: Child Token Exchange
PublisherServer->>DiscordAPI: POST partner-sdk/child-token (parent token + child app ID)
Note over DiscordAPI: Validation Process
DiscordAPI-->>PublisherServer: Return child access token (no refresh token)
PublisherServer->>GameClient: Send child access token
GameClient->>DiscordAPI: Establish SDK session with child token
DiscordAPI-->>GameClient: Session established
```
### Provisional Account Flow
```mermaid theme={"system"}
sequenceDiagram
participant User
participant GameClient
participant PublisherServer
participant DiscordAPI
Note over User, DiscordAPI: Provisional Account Authentication
User->>GameClient: Authenticate via game system
GameClient->>PublisherServer: Send game account credentials
PublisherServer->>DiscordAPI: POST partner-sdk/token/bot
(bot token + external_user_id)
DiscordAPI-->>PublisherServer: Parent access token (provisional account)
Note over User, DiscordAPI: Child Token Exchange
PublisherServer->>DiscordAPI: POST partner-sdk/child-token
(parent token + child app ID)
Note over DiscordAPI: Validation Process
DiscordAPI-->>PublisherServer: Return child access token (no refresh token)
PublisherServer->>GameClient: Send child access token
GameClient->>DiscordAPI: Establish SDK session with child token
DiscordAPI-->>GameClient: Session established
```
### Obtaining the Parent Access Token
Before you can exchange tokens, you must first obtain a parent access token through Discord OAuth2 authorization, or
provisional account authorization using the **publisher application ID**.
When performing authorization with publisher level account linking, always use the publisher application ID for
all authentication operations.
OAuth2 and Provisional account calls using child (game) application IDs will fail. Only the publisher application
can be used for user authorization.
Follow the standard [Account Linking with Discord](/developers/discord-social-sdk/development-guides/account-linking-with-discord)
or [Using Provisional Accounts](/developers/discord-social-sdk/development-guides/using-provisional-accounts)
flow, using your publisher application id. This will provide you with a parent access token and refresh token.
### Exchanging Parent Token for Child Token
Once you have a parent access token from the OAuth2 flow, exchange it for a child token using the `/partner-sdk/child-token` endpoint.
**Important Token Usage Restrictions**
Parent access tokens cannot be used to connect to Discord via the Social SDK - **only** child access tokens can
Child applications cannot be used for OAuth2 account linking flows - child access tokens can **only** be acquired through the token exchange flow
When making direct calls to Discord server APIs, use the child access token for Bearer authorization
#### Exchange for Child Token
```python theme={"system"}
import requests
API_ENDPOINT = 'https://discord.com/api/v10'
PARENT_ACCESS_TOKEN = 'parent_token_here'
CHILD_APPLICATION_ID = '987654321098765432'
PARENT_CLIENT_SECRET = 'your_parent_client_secret'
def exchange_for_child_token(parent_token, child_app_id, parent_client_secret):
response = requests.post(
f'{API_ENDPOINT}/partner-sdk/child-token',
headers={
'Content-Type': 'application/json'
},
json={
'parent_access_token': parent_token,
'child_application_id': child_app_id,
'parent_client_secret': parent_client_secret
}
)
response.raise_for_status()
return response.json()
# Exchange the token
child_token_data = exchange_for_child_token(
PARENT_ACCESS_TOKEN,
CHILD_APPLICATION_ID,
PARENT_CLIENT_SECRET
)
print(f"Child access token: {child_token_data['access_token']}")
print(f"Expires in: {child_token_data['expires_in']} seconds")
```
#### Token Exchange Response
```json theme={"system"}
{
"access_token": "child_application_access_token",
"token_type": "Bearer",
"expires_in": 604800,
"scope": "sdk.social_layer"
}
```
### Response Fields
| Field | Description |
| -------------- | ---------------------------------------------------------- |
| `access_token` | The child access token to use with the Social SDK |
| `token_type` | Always "Bearer" |
| `expires_in` | Token expiry time in seconds (matches parent token expiry) |
| `scope` | OAuth2 scopes for the token |
No refresh token is returned from the child token endpoint. When you need a new child token, re-exchange using the parent token.
***
## Integrating with the Social SDK
After completing the backend token exchange, you need to integrate the child token into your game client's Social SDK. This section covers how to configure the SDK to work with publisher authentication.
### Use the Publisher Application ID for Authorization
When your game needs to initiate authorization, always use the **publisher application ID**, not the game application ID:
```cpp theme={"system"}
// Use the PUBLISHER_APPLICATION_ID for authorization
discordpp::AuthorizationArgs args{};
args.SetClientId(PUBLISHER_APPLICATION_ID); // Parent app ID, not child
args.SetScopes(discordpp::Client::GetDefaultPresenceScopes());
client->Authorize(args, [](discordpp::ClientResult result, std::string code, std::string redirectUri) {
if (result.Successful()) {
std::cout << "✅ Authorization successful!" << std::endl;
// Exchange code for parent token, then exchange parent token for child token
}
});
```
### Connect the SDK with the Child Token
Once your backend provides the child token, use it to authenticate the Social SDK:
```cpp theme={"system"}
#include
void InitializeSDKWithChildToken(const std::string& childAccessToken) {
// Create SDK client instance
auto client = std::make_shared();
// Update the client with the child token
client->UpdateToken(
discordpp::AuthorizationTokenType::Bearer,
childAccessToken,
[client](discordpp::ClientResult result) {
if (result.Successful()) {
std::cout << "✅ Token updated successfully!" << std::endl;
// Connect to Discord
client->Connect();
} else {
std::cerr << "❌ Failed to update token: " << result.Error() << std::endl;
}
}
);
}
```
Once connected, all Social SDK operations execute in the context of the game (child) application.
***
## Token Lifecycles
Child game application token lifecycles are directly tied to their parent publisher application tokens. Understanding how game tokens behave when publisher tokens are refreshed or revoked is essential for implementing your integration correctly.
### Token Expiration
Game token expirations are tied to their parent publisher tokens:
* Game tokens expire at the **same time** as the publisher token
* Game token `expires_in` matches the publisher token's remaining lifetime
* No refresh tokens are provided for game tokens
### Publisher Token Refresh
When publisher application tokens are refreshed:
* Existing game tokens remain valid
* Game tokens will still expire at the original publisher token expiry time
* To get a new game token with updated expiry, re-exchange using the refreshed publisher token
### Publisher Token Revoked
When publisher application tokens are revoked:
* All associated child game tokens are immediately invalidated
* Any active SDK sessions using game tokens will be disconnected
***
## Revoking Access Tokens
When revoking tokens in a publisher-level account linking setup, it's important to understand which application ID to use for revocation.
### Token Issuance vs. Revocation
While game tokens are issued through the publisher application's authority, revocation follows a different pattern:
* **Game tokens are issued by the publisher application** - The `/partner-sdk/child-token` endpoint uses the publisher application's credentials to issue game tokens
* **Revocation uses the token's associated application ID**:
* **Publisher token revocation** → Use the **parent publisher application ID**
* **Game token revocation** → Use the **child game application ID**
When revoking a token, always use the application ID that the token represents, not the application ID that issued it.
### Revoking Publisher Tokens
To revoke a publisher access token, use the publisher application ID and credentials:
```python theme={"system"}
import requests
API_ENDPOINT = 'https://discord.com/api/v10'
PARENT_CLIENT_ID = 'YOUR_PARENT_CLIENT_ID'
PARENT_CLIENT_SECRET = 'YOUR_PARENT_CLIENT_SECRET'
def revoke_parent_token(parent_token):
data = {'token': parent_token}
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
r = requests.post(
f'{API_ENDPOINT}/oauth2/token/revoke',
data=data,
headers=headers,
auth=(PARENT_CLIENT_ID, PARENT_CLIENT_SECRET)
)
r.raise_for_status()
```
Revoking a publisher token will invalidate all publisher access and refresh tokens for that user, and will also **immediately invalidate all associated game tokens**. Any active SDK sessions using game tokens will be disconnected.
### Revoking Game Tokens
To revoke a game access token, use the game application ID and credentials:
```python theme={"system"}
import requests
API_ENDPOINT = 'https://discord.com/api/v10'
CHILD_CLIENT_ID = 'YOUR_CHILD_CLIENT_ID'
CHILD_CLIENT_SECRET = 'YOUR_CHILD_CLIENT_SECRET'
def revoke_child_token(child_token):
data = {'token': child_token}
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
r = requests.post(
f'{API_ENDPOINT}/oauth2/token/revoke',
data=data,
headers=headers,
auth=(CHILD_CLIENT_ID, CHILD_CLIENT_SECRET)
)
r.raise_for_status()
```
For more details on the token revocation API and additional options, see [Revoking Access Tokens](/developers/discord-social-sdk/development-guides/account-linking-with-discord#revoking-access-tokens) in the Account Linking guide.
***
## Alternatives for Publishers Without a Shared Social Graph
If your studio operates a centralized publisher account system but doesn't have a shared social graph across games, you can still build your own
centralized backend system that manages per-game Discord application IDs with the standard authentication model. This lets you reuse Social SDK integration logic across titles while keeping each game's Discord connection separate.
There is also potential for you to build a publisher account settings UI that lets players link, unlink, and manage which Discord account is connected to each game, giving players finer-grained control over their account linking experience.
The trade-off is that players will need to authorize each game individually, and it requires more development, but it also provides players more control over which games they link to their Discord accounts – which may well make more
sense for your individual studio or game specific needs.
***
## Next Steps
Now that you've set up publisher level account linking, explore more features of the Discord Social SDK:
}>
Combine Discord and game friends into a single list for easy management.
}>
Display game status and information to Discord friends.
}>
Bring players together in a shared lobby with invites, text chat, and voice comms.
Need help? Join the [Discord Developers Server](https://discord.gg/discord-developers) and share questions in the `#social-sdk-dev-help` channel for support from the community.
If you encounter a bug while working with the Social SDK, please report it here: [https://dis.gd/social-sdk-bug-report](https://dis.gd/social-sdk-bug-report)
***
## Change Log
| Date | Changes |
| ----------------- | --------------- |
| Febraury 18, 2026 | Initial release |
# Sending Direct Messages
Source: https://docs.discord.com/developers/discord-social-sdk/development-guides/sending-direct-messages
Send direct messages to Discord users from your application.
This feature is currently available with rate limits.
To increase the rate limits for your game, please follow
[Communication Features: Applying for Increased Rate Limits for Production Releases](/developers/discord-social-sdk/core-concepts/communication-features#applying-for-increased-rate-limits-for-production-releases).
## Overview
Direct Messages (DMs) allow players to communicate privately. This guide will show you how to:
* Send text messages between users
* Handle displaying messages in your game
* Retrieve conversation history and summaries
### Prerequisites
Before you begin, make sure you have:
* Set up the Discord Social SDK with our [Getting Started guide](/developers/discord-social-sdk/getting-started)
To utilize this communication feature, you must enable [`Client::GetDefaultCommunicationScopes`] in your OAuth Scope configuration.
See the [OAuth Scopes Core Concepts Guide](/developers/discord-social-sdk/core-concepts/oauth2-scopes) for more details.
[`Client::GetDefaultCommunicationScopes`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a71499da752fbdc2d4326ae0fd36c0dd1
### Types of Chat Messages
The Discord Social SDK supports two types of chat:
* [Direct messages (DMs) between two users](/developers/discord-social-sdk/development-guides/sending-direct-messages#sending-a-direct-message-to-a-user)
* [Chat messages within a lobby](/developers/discord-social-sdk/development-guides/managing-lobbies#sending-messages-to-a-lobby)
The SDK receives messages in the following channel types:
* DM
* Ephemeral DM
* Lobby
Let's get started with sending a direct message to another user.
***
## Sending a Direct Message to a User
While the SDK allows you to send messages on behalf of a user, you must only do so in response to a user action. You should never automatically send messages.
Here's how to send a direct message. You'll need the recipient's Discord ID and the message you want to send.
```cpp theme={"system"}
std::string message = "ready to queue?";
uint64_t recipientId = 1234567890; // The recipient's Discord ID
client->SendUserMessage(recipientId, message, [](auto result, uint64_t messageId) {
if (result.Successful()) {
std::cout << "✅ Message sent successfully\n";
} else {
std::cout << "❌ Failed to send message: " << result.Error() << "\n";
}
});
```
### Syncing Messages with Discord
In some situations, messages from your game with the Social SDK will also appear in Discord. This will happen for:
* 1 on 1 chat when at least one of the users is a full Discord user
* Lobby chat when the lobby is [linked to a Discord channel](/developers/discord-social-sdk/development-guides/linked-channels)
* The message must have been sent by a user who is not banned on Discord.
When messaging between provisional accounts or non-friends, channel ID and recipient ID is set to the other user's ID. These messages are sent ephemerally and do not persist within a channel. Because of that, you will not be able to resolve a [`ChannelHandle`] for them.
***
## Receiving and Rendering Messages
When a user sends a message, the SDK can respond to new messages by registering [`Client::SetMessageCreatedCallback`]. You can access the message content and sender's ID in your callback from the [`MessageHandle`] object.
The [`MessageHandle`] represents a single message received by the SDK.
```cpp theme={"system"}
client->SetMessageCreatedCallback([&client](uint64_t messageId) {
if (auto message = client->GetMessageHandle(messageId)) {
std::cout << "New message from " << message->AuthorId() << ": " << message->Content() << "\n";
}
});
```
There are also callbacks for when a message is updated or deleted. Register these callbacks with [`Client::SetMessageUpdatedCallback`] and [`Client::SetMessageDeletedCallback`].
### Suppressing Double Notifications
Suppose the user has the Discord desktop application open on the same machine as the game. In that case, they will hear notifications from the Discord application, even though they can see those messages in-game. So to avoid double-notifying users, you should call [`Client::SetShowingChat`] whenever the chat is shown or hidden to suppress those duplicate notifications.
### Legal Disclosure Message Type
As a convenience for game developers, the first time a user sends a message in the game, that message shows up on the Discord client. The SDK will inject a "fake" message into the chat that contains a basic English explanation of what is happening to the user. You can identify these messages with the [`MessageHandle::DisclosureType`] method. We encourage you to customize the rendering of these messages, possibly changing the wording, translating them, and making them look more "official". You can choose to avoid rendering these as well.
### Message History
The SDK keeps the 25 most recent messages in each channel in memory, including direct messages. For older messages,
see the [Getting Direct Message History](/developers/discord-social-sdk/development-guides/sending-direct-messages#getting-direct-message-history)
section below to retrieve additional history from Discord's servers.
A [`MessageHandle`] will keep working even after the SDK has discarded the message for being too old.
You just won't be able to create new [`MessageHandle`] objects for that message via [`Client::GetMessageHandle`].
### Working with Unrenderable Content
Messages sent on Discord can contain content that may not be renderable in-game, such as images, videos, embeds, polls, and more. The game isn't expected to render these. Instead, it should show a notice so the user knows there is more content and a way to view it on Discord. The [`MessageHandle::AdditionalContent`] method will contain data about the non-text content in this message.
You can use this metadata to render a placeholder message for players and can link out to Discord using [`Client::CanOpenMessageInDiscord`] and [`Client::OpenMessageInDiscord`].
There is also more information about [messages in the Discord API](/developers/resources/message) documentation.
## Getting Direct Message History
The SDK provides two methods to retrieve direct message conversation history, allowing you to display past conversations when users open a DM or browse their message list.
### Retrieving All Conversation Summaries
Use [`Client::GetUserMessageSummaries`] to get a list of all users the current user has DM conversations with, along
with the most recent message ID for each conversation:
```cpp theme={"system"}
client->GetUserMessageSummaries([](const discordpp::ClientResult &result,
const std::vector &summaries) {
if (result.Successful()) {
std::cout << "📋 Retrieved " << summaries.size() << " conversations\n";
for (const auto &summary : summaries) {
std::cout << "User ID: " << summary.UserId()
<< ", Last Message ID: " << summary.LastMessageId() << "\n";
}
} else {
std::cerr << "❌ Failed to retrieve conversation summaries\n";
}
});
```
This is particularly useful for:
* Building a conversation list UI
* Determining which users have active conversations
* Finding the most recent activity in each conversation
### Retrieving Messages from a Specific Conversation
Use [`Client::GetUserMessagesWithLimit`] to fetch message history from a specific DM conversation:
```cpp theme={"system"}
const uint64_t recipientId = 1234567890; // The other user's Discord ID
const int32_t messageLimit = 50; // Number of recent messages to retrieve (max 200)
client->GetUserMessagesWithLimit(
recipientId, messageLimit,
[](const discordpp::ClientResult &result,
const std::vector &messages) {
if (result.Successful()) {
std::cout << "💬 Retrieved " << messages.size()
<< " messages from conversation\n";
// Messages are returned in reverse chronological order (newest first)
for (const auto &message : messages) {
std::cout << "Message: " << message.Content()
<< " from " << message.AuthorId() << std::endl;
}
} else {
std::cerr << "❌ Failed to retrieve message history\n";
}
}
);
```
**Important limitations:**
* Only a maximum of 200 messages and up to 72 hours of history can be retrieved
* Both players must have played the game for DM history to be accessible
* If either user hasn't played the game, the system cannot find a channel between them and may return a 404 `discordpp::ErrorType::HTTPError` error
Key points about [`Client::GetUserMessagesWithLimit`] that are important to note:
* Messages are returned in reverse chronological order (newest first)
* The function checks the local cache first and only makes an HTTP request if needed
* Pass 0 or a negative value for `limit` to retrieve all available messages (up to the 200 message maximum)
This functionality is useful for:
* Displaying conversation history when a user opens a DM
* Implementing message scrollback features
* Preserving conversation context across game sessions
* Building a full-featured in-game messaging interface
## In-Game Direct Message Settings
The Discord client provides a settings screen for users to be able to control who can DM them in-game via the
Social SDK.
You cannot control these settings directly with the Social SDK. However, you can call
[`Client::OpenConnectedGamesSettingsInDiscord`], which opens the Connected Games settings in the Discord client,
where users can manage their direct messaging settings related to games using the Discord Social SDK.
If the client isn't connected or the user is a provisional account, this function does nothing.
It is always a no-op for console platforms.
***
## Development Rate Limits
Prior to being approved for [increased rate limits](/developers/discord-social-sdk/core-concepts/communication-features#applying-for-increased-rate-limits-for-production-releases), sending direct messages is limited to **100 per 2 hours** per application.
These limitations are designed to provide sufficient capacity for development, testing, and small-scale demos while ensuring system stability.
For guidance on detecting and retrying rate limited requests in the SDK, see [Handle Rate Limits](/developers/discord-social-sdk/how-to/handle-rate-limits).
***
## Next Steps
Now that you know how to send and receive messages, check out these other Discord Social SDK features:
}>
Display in-game status and activity to friends.
}>
Bring players together in a shared lobby with invites, text chat, and voice comms.
}>
Enable text chat between a Discord channel and your game.
Need help? Join the [Discord Developers Server](https://discord.gg/discord-developers) and share questions in the `#social-sdk-dev-help` channel for support from the community.
If you encounter a bug while working with the Social SDK, please report it here: [https://dis.gd/social-sdk-bug-report](https://dis.gd/social-sdk-bug-report)
***
## Change Log
| Date | Changes |
| --------------- | -------------------------------- |
| August 29, 2025 | Add DM history |
| June 30, 2025 | Add communications scope warning |
| May 06, 2025 | link to DM Settings |
| March 17, 2025 | initial release |
[`ChannelHandle`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ChannelHandle.html#ac32096b2ef15c5c220e9b7b92253cc46
[`Client::CanOpenMessageInDiscord`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ae2aac143a691091691c5cc75aa07dace
[`Client::GetMessageHandle`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a7825220b28952a2156bd0e46db40ea5c
[`Client::GetUserMessageSummaries`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a32dafc20ff1f99b019e40bdc81f46dde
[`Client::GetUserMessagesWithLimit`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a054a758a76c5873b38a4d79651a5f26c
[`Client::OpenConnectedGamesSettingsInDiscord`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a24f268f5eebe9919a3f774354eb577e0
[`Client::OpenMessageInDiscord`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a66b8f85b14403a5d5ea125f39aa6e1b1
[`Client::SetMessageCreatedCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a28325a8e8c688a84ac851da4bc86e148
[`Client::SetMessageDeletedCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a2b6079eded10bea29abbb9695702637b
[`Client::SetMessageUpdatedCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aa01cf3c15403f29780dabfcfaf3b1dcd
[`Client::SetShowingChat`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#acdf400ecb926392d1a110da73152b594
[`MessageHandle`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#ae25595b43bc74b0c4c92c5165d16382f
[`MessageHandle::AdditionalContent`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#af4497491a95fda65402b6acf7a8b42e5
[`MessageHandle::DisclosureType`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#abefb9be7836951a6acf78a4bb1676638
# Setting Rich Presence
Source: https://docs.discord.com/developers/discord-social-sdk/development-guides/setting-rich-presence
Update Discord Rich Presence from your application to display activity information.
## Overview
Rich Presence allows you to display detailed information about what players are doing in your game. Users can see this information in their Discord profile and friends list and use it to join their friends' games with Game Invites.
### Prerequisites
Before you begin, make sure you have:
* [Set up the Discord Social SDK](/developers/discord-social-sdk/getting-started)
* Connected to Discord with a valid client instance
This feature requires the **Default Presence Scopes** (`openid` and `sdk.social_layer_presence`).
Use [`Client::GetDefaultPresenceScopes`] when configuring your OAuth2 flow.
See the [OAuth2 Scopes guide](/developers/discord-social-sdk/core-concepts/oauth2-scopes) for details on all available scopes.
[`Client::GetDefaultPresenceScopes`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a7648bd1d2f7d9a86ebd0edb8bef12b5c
***
## Understanding Rich Presence
Rich Presence allows you to display detailed information about players' actions in your game. Users can see this information in various places in Discord, including:
* User profiles
* Friend lists
* Server member lists
Let's talk about the naming of some Discord primitives first. Rich Presence, aka "Activity", can be thought of as the "current activity of a user" and is represented by the [`Activity`] class in the SDK and [in our gateway events](/developers/events/gateway-events#activity-object). This is not to be confused with [Discord Activities](/developers/activities/overview), which are embedded games that can also set and display rich presence.
Each [`Activity`] contains fields that describe the following:
| Field | Description | Purpose |
| -------------------- | ------------------------ | ----------------------------------------------------------------------------------------------------------- |
| `name` | Game or app name | Displayed in the user's profile |
| `type` | Activity type | What the player is doing (e.g., "Playing", "Watching", "Listening") |
| `details` | What the player is doing | Main activity description (e.g., "Playing Capture the Flag") |
| `state` | Their current status | Secondary status (e.g., "In Queue", "In Match, "In a group") |
| `party` | Party information | Shows party size and capacity (e.g., "2 of 4") |
| `timestamps` | Activity duration | Shows elapsed or remaining time |
| `assets` | Custom artwork | Game/map thumbnails and character icons |
| `secrets` | Join/spectate tokens | Enable [Game Invite](/developers/discord-social-sdk/development-guides/managing-game-invites) functionality |
| `supportedPlatforms` | Platform flags | Control where join buttons appear |
See below for examples of how to set these fields in your code.
While we support multiple [`ActivityTypes`], games should use `ActivityTypes::Playing` for `type`. The SDK automatically associates the activity with your game, so by default fields like `name` show your game's name. If you'd like to customize the displayed name, see [Setting the Application Name](/developers/discord-social-sdk/development-guides/setting-rich-presence#setting-the-application-name).
### Customizing Rich Presence
When displayed in Discord, Rich Presence has three main components:
```
Playing "Your Game Name" <- Line 1: Game name (automatic)
Capture the Flag | 2 - 1 <- Line 2: Details field
In a group (2 of 3) <- Line 3: State + Party info
```
You can control how lines 2 and 3 are rendered in Discord, here's the breakdown:
* Line 1, `Playing "game name"` is powered by the name of your game (or application) on Discord.
* Line 2, `Capture the flag | 2 - 1` is powered by the `details` field in the activity, and this should generally try to describe what the *player* is currently doing. You can even include dynamic data such as a match score here.
* Line 3, `In a group (2 of 3)` describes the *party* the player is in. "Party" is used to refer to a group of players in a shared context, such as a lobby, server, team, etc. The first half, `In a group` is powered by the state field in the activity, and the second half, `(2 of 3)` is powered by the party field in the activity and describes how many people are in the current party and how big the party can get.
This diagram visually shows the field mapping:
For tips on designing Rich Presence, take a look at the [Rich Presence best practices guide](/developers/rich-presence/best-practices).
***
## Setting an Invite Image
The Rich Presence invite image appears when invites are sent for a 3rd party game or app using the Discord Social SDK. After uploading an invite image for your app, you can see a preview of it to the right (under "IRL Invite Image Example").
***
## Uploading Assets
While integrating Rich Presence, you'll likely want to upload custom art assets for your app. For all Rich Presence assets, it's highly recommended to make them 1024 x 1024.
To add custom assets for Rich Presence, navigate to your app's settings and click Rich Presence on the left-hand sidebar. On the [Art Assets page](https://discord.com/developers/applications/select/rich-presence/assets), you can upload two different types of assets.
Up to 300 custom assets can be added to your app for later use when setting Rich Presence for a Discord user. These assets can be anything that help orient others to what a user is doing inside of your Activity or 3rd party game.
If you need more than 300 custom assets or want to use images stored somewhere else, you can also [specify an external URL](/developers/events/gateway-events#activity-object-activity-asset-image) as long it still has the proper dimensions and size. Unlike uploaded assets, external URLs also support GIF, animated WebP, and AVIF.
For tips on choosing assets, take a look at the [Rich Presence best practices guide](/developers/rich-presence/best-practices#have-interesting-expressive-art).
When uploading Rich Presence assets, **the asset keys will automatically be changed to lowercase**. You can see this reflected in your app's settings after saving a newly uploaded asset, and you should keep it in mind when referencing any asset keys in your code.
Once you've uploaded these assets, you can use the asset key to reference them in your code when [Setting Assets in Rich Presence](/developers/discord-social-sdk/development-guides/setting-rich-presence#setting-assets).
***
## Setting Details and State
Here's a simple example setting the details and state of a Rich Presence activity:
```cpp theme={"system"}
// Create a new activity
discordpp::Activity activity;
activity.SetType(discordpp::ActivityTypes::Playing);
activity.SetDetails("Battle Creek");
activity.SetState("In Competitive Match");
// Update the presence
client->UpdateRichPresence(activity, [](discordpp::ClientResult result) {
if (result.Successful()) {
std::cout << "✅ Rich presence updated!\n";
}
});
```
***
## Setting the Application Name
By default, Rich Presence displays your application's name as it's registered on Discord (the top line of Rich Presence, e.g. `Playing "My Game: Chapter Two"`). You can override this displayed name by setting the `name` field on the activity with [`Activity::SetName`] before calling [`Client::UpdateRichPresence`]:
```cpp theme={"system"}
// Override the displayed application name (top line of Rich Presence)
activity.SetName("My Game: Chapter Two");
```
Registered application names are restricted to a limited character set. Using [`Activity::SetName`] gives you more flexibility over exactly what text is displayed, including characters that aren't permitted in your registered application name.
***
## Setting Timestamps
You can include timestamps in your Rich Presence to display a live timer, such as how long a player has been in their current activity or how much time is left in a timed match. Timestamps are [`ActivityTimestamps`] set as Unix timestamps in seconds.
The direction of the timer is determined by which field you set:
* Set the **start** time only to count **up** and show elapsed time (e.g. `12:34 elapsed`).
* Set the **end** time to count **down** and show the remaining time (e.g. `12:34 left`).
### Count up (elapsed time)
To show how long the player has been in the activity, set [`ActivityTimestamps::SetStart`] to a time in the past, such as the current time. The timer counts up from that point.
```cpp theme={"system"}
// Counting up: elapsed time since the activity started
discordpp::ActivityTimestamps timestamps;
// time(nullptr) returns the current Unix time in seconds, so the timer starts now
timestamps.SetStart(time(nullptr));
activity.SetTimestamps(timestamps);
```
### Count down (time remaining)
To show how long until a timed activity ends, set [`ActivityTimestamps::SetEnd`] to a time in the future. The timer counts down to that point.
```cpp theme={"system"}
// Counting down: time remaining until the activity ends (one hour from now)
discordpp::ActivityTimestamps timestamps;
// time(nullptr) is the current Unix time in seconds; + 3600 sets the end one hour from now
timestamps.SetEnd(time(nullptr) + 3600);
activity.SetTimestamps(timestamps);
```
Setting the **end** time is what makes the timer count down. If you only set the **start** time, the timer counts up instead. You can set both — `start` still controls the elapsed reference point, but as long as `end` is set the timer displays the remaining time.
***
## Setting Assets
Once you've uploaded assets to your app, you can reference them using their **asset key** in your code to set custom artwork in Rich Presence. Here's an example of an asset with the key of "map-mainframe", "tank-avatar", and "invite-cover-image":
```cpp theme={"system"}
// Setting Activity Assets
discordpp::ActivityAssets assets;
assets.SetLargeImage("map-mainframe");
assets.SetLargeText("Mainframe");
assets.SetSmallImage("tank-avatar");
assets.SetSmallText("Tank");
assets.SetInviteCoverImage("invite-cover-image"); // Used for Game Invites
activity.SetAssets(assets);
```
If you need more than 300 custom assets or want to use images stored somewhere else, you can also [specify an external URL](/developers/events/gateway-events#activity-object-activity-asset-image) as long it still has the proper dimensions and size.
Unlike uploaded assets, external URLs also support GIF, animated WebP, and AVIF.
***
## Setting Field URLs
You can set URLs for `details`, `state`, `assets.large_image` and `assets.small_image` in Rich Presence. When present, these URLs will make the corresponding image/text into clickable links.
```cpp theme={"system"}
activity.SetState("Playing on Mainframe");
activity.SetStateUrl("https://example.com/maps/mainframe");
activity.SetDetails("Rank #1337 in global leaderboard");
activity.SetDetailsUrl("https://example.com/leaderboard/global");
discordpp::ActivityAssets assets;
assets.SetLargeImage("map-mainframe");
assets.SetLargeText("Mainframe");
assets.SetLargeUrl("https://example.com/maps/mainframe");
assets.SetSmallImage("tank-avatar");
assets.SetSmallText("Tank");
assets.SetSmallUrl("https://example.com/classes/tank");
activity.SetAssets(assets);
```
***
## Setting Buttons
You can add up to two custom buttons to a player's Rich Presence. Each button has a label and a URL, making them a direct call to action for anyone viewing the presence — link out to your game's store page, website, or community server.
```cpp C++ theme={"system"}
discordpp::ActivityButton buyButton;
buyButton.SetLabel("Buy On Steam!");
buyButton.SetUrl("https://store.steampowered.com/app/AppID/GameName/");
discordpp::ActivityButton communityButton;
communityButton.SetLabel("Join the Community!");
communityButton.SetUrl("https://discord.gg/your-discord-url-or-id");
activity.AddButton(buyButton);
activity.AddButton(communityButton);
```
```csharp Unity theme={"system"}
ActivityButton buyButton = new ActivityButton();
buyButton.SetLabel("Buy On Steam!");
buyButton.SetUrl("https://store.steampowered.com/app/AppID/GameName/");
ActivityButton communityButton = new ActivityButton();
communityButton.SetLabel("Join the Community!");
communityButton.SetUrl("https://discord.gg/your-discord-url-or-id");
activity.AddButton(buyButton);
activity.AddButton(communityButton);
```
```cpp Unreal theme={"system"}
UDiscordActivityButton* BuyButton = NewObject();
BuyButton->Init();
BuyButton->SetLabel("Buy On Steam!");
BuyButton->SetUrl("https://store.steampowered.com/app/AppID/GameName/");
UDiscordActivityButton* CommunityButton = NewObject();
CommunityButton->Init();
CommunityButton->SetLabel("Join the Community!");
CommunityButton->SetUrl("https://discord.gg/your-discord-url-or-id");
Activity->AddButton(BuyButton);
Activity->AddButton(CommunityButton);
```
Buttons are only visible to **other users** — you cannot see buttons on your own Rich Presence. To test that your buttons are working, use a second account or ask a friend to view your profile.
***
## Configuring Status Text
By default, Rich Presence will display the game's name in the user's status text. You can override this behavior by setting a status display type.
```cpp theme={"system"}
// uses the game's name in the status text (default)
activity.SetStatusDisplayType(discordpp::StatusDisplayTypes::Name);
// uses the activity's state field in the status text
activity.SetStatusDisplayType(discordpp::StatusDisplayTypes::State);
// uses the activity's details field in the status text
activity.SetStatusDisplayType(discordpp::StatusDisplayTypes::Details);
```
***
## Setting Party and Join Secret
You can also include party details and a join secret in your Rich Presence to power Game Invites. Check out the [Game Invites guide](/developers/discord-social-sdk/development-guides/managing-game-invites) for more information.
```cpp theme={"system"}
// Setting Party Details
discordpp::ActivityParty party;
party.SetId("party1234");
party.SetCurrentSize(1);
party.SetMaxSize(5);
activity.SetParty(party);
// Setting Join Secret details
discordpp::ActivitySecrets secrets;
secrets.SetJoin("your-join-secret");
activity.SetSecrets(secrets);
```
***
## Setting Supported Platforms
You can set the supported platforms for your game in Rich Presence. This will control where the join buttons appear in Discord.
If you only want the join button to appear on desktop, you can set the supported platforms like this:
```cpp theme={"system"}
activity.SetSupportedPlatforms(discordpp::ActivityGamePlatforms::Desktop);
```
See the `ActivityGamePlatforms` enum for all supported platforms.
## Rich Presence Without Authentication
Rich Presence via RPC (Remote Procedure Call) will only work with a running Discord desktop client. It does not support mobile, console or web clients.
Unlike most other features of the Discord Social SDK, **Rich Presence can be set without authentication**. Instead of
using [`Client::Connect`] to authenticate with Discord, you can use Rich Presence functionality by directly communicating
with a running Discord desktop client through RPC (Remote Procedure Call).
### Requirements
* Discord desktop client must be running on the user's machine
* Your application must be registered with Discord and have a valid Application ID
This direct approach makes Rich Presence integration much simpler for developers who only need basic presence
functionality while Discord desktop clients are running.
### Setting Up Direct Rich Presence
To use Rich Presence without authentication, simply:
1. Set your application ID using [`Client::SetApplicationId`]
2. Configure your activity details
3. Call [`Client::UpdateRichPresence`]
```cpp theme={"system"}
auto client = std::make_shared();
// Set the application ID (no Connect() call needed)
client->SetApplicationId(APPLICATION_ID);
// Configure rich presence details
discordpp::Activity activity;
activity.SetType(discordpp::ActivityTypes::Playing);
activity.SetState("In Competitive Match");
activity.SetDetails("Rank: Diamond II");
// Update rich presence
client->UpdateRichPresence(
activity, [](const discordpp::ClientResult &result) {
if (result.Successful()) {
std::cout << "🎮 Rich Presence updated successfully!\n";
} else {
std::cerr << "❌ Rich Presence update failed";
}
});
```
***
## Next Steps
Now that you've set up Rich Presence, you might want to explore:
}>
Allow players to invite friends to join their game session or party.
}>
Bring players together in a shared lobby with invites, text chat, and voice comms.
}>
Best practices for Rich Presence UI/UX.
Need help? Join the [Discord Developers Server](https://discord.gg/discord-developers) and share questions in the `#social-sdk-dev-help` channel for support from the community.
If you encounter a bug while working with the Social SDK, please report it here: [https://dis.gd/social-sdk-bug-report](https://dis.gd/social-sdk-bug-report)
***
## Change Log
| Date | Changes |
| -------------- | ------------------------------------------- |
| June 24, 2026 | Clarified count-up vs count-down timestamps |
| June 5, 2026 | Added Setting the Application Name section |
| March 31, 2026 | Added Setting Buttons section |
| March 17, 2025 | Initial release |
[`Activity`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#ae793d9adbe16fef402b859ba02bee682
[`Activity::SetName`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#a59f9a63a8b105946d0c9838f3e643ae2
[`ActivityTimestamps`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityTimestamps.html#a0ff108aac69639c18f1669994e459ee2
[`ActivityTimestamps::SetEnd`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityTimestamps.html#ab6478ec46860feb64174da3eb1063aaa
[`ActivityTimestamps::SetStart`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityTimestamps.html#a5bb6a6bc243fedb954ae82d6c4ef3542
[`ActivityTypes`]: https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#a6c76a8cbbc9270f025fd6854d5558660
[`Client::Connect`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a873a844c7c4c72e9e693419bb3e290aa
[`Client::SetApplicationId`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ad452335c06b28be0406dab824acccc49
[`Client::UpdateRichPresence`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#af0a85e30f2b3d8a0b502fd23744ee58e
# Using Provisional Accounts
Source: https://docs.discord.com/developers/discord-social-sdk/development-guides/using-provisional-accounts
Implement provisional Discord accounts for users who don't have Discord accounts yet.
## Overview
Provisional accounts let players use Social SDK features in your game without linking a Discord account so all players can have a consistent gameplay experience.
With provisional accounts, players can:
* Add friends and communicate with other players
* Join voice chats in game lobbies
* Send direct messages to other players
* Appear in friends lists and game lobbies
All of this works seamlessly whether your players have Discord accounts or not.
This guide will show you how to:
1. Set up provisional accounts for your game
2. Create and manage provisional accounts
3. Handle account merging when users want to upgrade to full Discord
## Prerequisites
Before you begin, make sure you have:
* A basic understanding of how the SDK works from the [Getting Started Guide](/developers/discord-social-sdk/getting-started)
* An external authentication provider set up for your game
***
## What Are Provisional Accounts?
Think of provisional accounts as temporary Discord accounts that:
* Work only with your game
* Can be upgraded to full Discord accounts later
* Persist between game sessions
* Use your game's authentication system
With provisional accounts, players can use Discord features like chat and voice and interact with game friends without creating a full Discord account. They are "placeholder" Discord accounts for the user that your game owns and manages.
For existing Discord users who have added a provisional account as a game friend, the provisional account will appear in their friend list, allowing you to send direct messages and interact with them for text and voice in lobbies.
### How It Works
1. Your game authenticates players using your existing system (Steam, Epic, etc.)
2. Discord creates temporary accounts linked to those identities
3. Players can use Discord features immediately
4. Players can optionally upgrade to full Discord accounts later
5. All friends and chat history transfer when upgrading
### Benefits
* Instant Access: Players can use social features immediately
* Seamless Experience: Works the same for all players
* Easy Upgrade Path: Simple conversion to full Discord accounts
* Data Persistence: Friends and history are preserved
* Cross-Platform: Works on all supported platforms
***
## Getting Set Up
### Choosing an Authentication Method
Discord offers a number of authentication methods, the one you use depends on how you game and account system is set up:
1. Use the [Bot Token Endpoint](/developers/discord-social-sdk/development-guides/using-provisional-accounts#server-authentication-with-bot-token-endpoint) if your game has an account system which uniquely identifies users. This is the recommended approach when possible.
2. Use the [Server Authentication with External Credentials Exchange](/developers/discord-social-sdk/development-guides/using-provisional-accounts#server-authentication-with-external-credentials-exchange) if you have an existing OIDC provider, or do not have an account system.
3. Use the [Client Side Token Exchange Method](/developers/discord-social-sdk/development-guides/using-provisional-accounts#authentication-for-public-clients) if you are using a Public Client.
If you are using (2) or (3), you must configure you identity provider before being able to create provisional accounts.
### Configuring Your Identity Provider
If you are using the bot token endpoint, no Identity Provider configuration is required.
Open the Discord app for your game in the [Developer Portal](https://discord.com/developers/applications). Find the [External Auth](https://discord.com/developers/applications/select/social-sdk/external-auth-providers) page under the `Discord Social SDK` section in the sidebar.
Click on `Add Auth Provider` and choose the type of provider you're using (Steam, OIDC, etc.). Fill in the required details for your provider.
We currently support the following provider types:
* OpenID Connect (OIDC)
* Steam Session Tickets
* Epic Online Services (EOS)
* Unity
* Apple
* PlayStation Network (PSN)
If you are configuring OIDC, see [OIDC Integration Requirements](#oidc-integration-requirements) for the full list of requirements your issuer URL, discovery document, and ID tokens must meet.
Providers are represented in Discord's systems by the following types:
#### External Auth Types
| Type | Description |
| ------------------------------------- | ----------------------------------------------------------------------------- |
| OIDC | OpenID Connect ID token |
| STEAM\_SESSION\_TICKET | A Steam auth ticket for web generated with discord as the identity |
| EPIC\_ONLINE\_SERVICES\_ACCESS\_TOKEN | Access token for Epic Online Services. Supports EOS Auth access tokens |
| EPIC\_ONLINE\_SERVICES\_ID\_TOKEN | ID token for Epic Online Services. Supports both EOS Auth + Connect ID tokens |
| UNITY\_SERVICES\_ID\_TOKEN | Unity Services authentication ID token |
| APPLE\_ID\_TOKEN | Apple sign-in authentication ID token |
| PLAYSTATION\_NETWORK\_ID\_TOKEN | PlayStation Network account authentication ID token |
| DISCORD\_BOT\_ISSUED\_ACCESS\_TOKEN | An access token for a user authenticated via the Bot Token Endpoint |
***
## Implementing Provisional Accounts
Creating a Provisional Account and requesting an Access Token for the account always happens in a single step.
You provide external authentication and uniquely identifies the user, and Discord finds a user associated with that identifier.
* If there is no account associated with the identity, a new provisional account is created along with a new access token for the user.
* If there is a provisional account associated with the identity, an access token is returned.
* If there is an existing *full Discord account* associated with the identity, the request is aborted (See [Error Handling](/developers/discord-social-sdk/development-guides/using-provisional-accounts#error-handling)).
Once authentication is complete, you can use the access token as you would a full Discord user's access token.
### Server Authentication with Bot Token Endpoint
This is the preferred method of authentication. It ends up being the simplest choice for most provisional account integrations.
```python theme={"system"}
# filepath: your_game/server/auth.py
import requests
from models import GameAccount
def get_provisional_token(game_account: GameAccount):
response = requests.post(
'https://discord.com/api/v10/partner-sdk/token/bot',
headers={
'Content-Type': 'application/json',
'Authorization': 'Bot ' # your application's bot token
},
json={
'external_user_id': game_account.id, # your account system's unique id
'preferred_global_name': game_account.display_name, # your account system's display name for the user
}
)
return response.json()
```
#### Bot Token Endpoint Response
```python theme={"system"}
{
"access_token": "",
"id_token": "",
"token_type": "Bearer",
"expires_in": 604800,
"scope": "sdk.social_layer"
}
```
### Server Authentication with External Credentials Exchange
```python theme={"system"}
# filepath: your_game/server/auth.py
import requests
def get_provisional_token(external_token: str):
response = requests.post(
'https://discord.com/api/v10/partner-sdk/token',
json={
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET,
'external_auth_type': EXTERNAL_AUTH_TYPE, # See External Auth Types above
'external_auth_token': external_token
}
)
return response.json()
```
#### External Credentials Exchange Response
```python theme={"system"}
{
"access_token": "",
"id_token": "",
"token_type": "Bearer",
"expires_in": 604800,
"scope": "sdk.social_layer"
}
```
If you are using OIDC, you may see a `refresh_token` in this response. Using it via the OAuth2 `refresh_token` grant is **deprecated** — re-authenticate using a fresh provider token instead. See [Refreshing Provisional Account Tokens](#refreshing-provisional-account-tokens) for details.
### Authentication for Public Clients
This method requires enabling **Public Client** for your app. Most games will not want to ship with this enabled. [Learn more](/developers/discord-social-sdk/core-concepts/oauth2-scopes#oauth2-client-types)
If you have `Public Client` enabled on your Discord app, you can use the following code to authenticate your players with the external provider.
```cpp theme={"system"}
// filepath: your_game/auth_manager.cpp
void AuthenticateUser(std::shared_ptr client) {
// Get your external auth token (Steam, OIDC, etc.)
std::string externalToken = GetExternalAuthToken();
// Get provisional token from Discord
client->GetProvisionalToken(DISCORD_APPLICATION_ID,
discordpp::AuthenticationExternalAuthType::OIDC,
externalToken,
[client](discordpp::ClientResult result, std::string accessToken, std::string refreshToken, discordpp::AuthorizationTokenType tokenType, int32_t expiresIn, std::string scope) {
if (result.Successful()) {
std::cout << "🔓 Provisional token received! Establishing connection...\n";
client->UpdateToken(discordpp::AuthorizationTokenType::Bearer, accessToken, [client](discordpp::ClientResult result) {
client->Connect();
});
} else {
std::cerr << "❌ Provisional token request failed: " << result.Error() << std::endl;
}
});
}
```
### OIDC Integration Requirements
If you are using OpenID Connect (OIDC) as your identity provider, Discord validates your configuration and tokens against the requirements below. Meeting these requirements is necessary both when saving your OIDC configuration in the [Developer Portal](https://discord.com/developers/applications/select/social-sdk/external-auth-providers) and at runtime when tokens are exchanged.
#### Issuer URL Requirements
The issuer URL you configure in the Developer Portal must meet all of the following:
| Requirement | Details |
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| HTTPS scheme | Must use `https://` — HTTP is not permitted |
| No query parameters | The URL must not contain a `?` character |
| No fragment | The URL must not contain a `#` character |
| No embedded credentials | The URL must not contain a username or password |
| Public hostname | The hostname must have at least two segments (e.g. `example.com`). Bare hostnames such as `localhost` are not permitted |
| No private or reserved TLDs | The hostname must not end in `.local`, `.arpa`, `.internal`, or `.localhost` |
| No IP addresses | The hostname must not be a bare IP address (e.g. `192.168.1.1`) |
Non-standard ports (e.g. `:8080`) are permitted.
#### OIDC Discovery Document Requirements
Discord fetches your OIDC configuration from `{issuer_url}/.well-known/openid-configuration` per [RFC 8414](https://www.rfc-editor.org/rfc/rfc8414). This endpoint must:
* Be accessible over HTTPS
* **Not require HTTP redirects** — Discord does not follow redirects when fetching this document or your JWKS endpoint
* Return a valid JSON object (not an array)
The discovery document must include these fields:
| Field | Type | Requirement |
| --------------------------------------- | ---------------- | ------------------------------------------------------------------------------ |
| `issuer` | HTTPS URL | Must exactly match the issuer URL used to fetch the document |
| `jwks_uri` | HTTPS URL | URI to your JWKS signing key endpoint |
| `id_token_signing_alg_values_supported` | Array of strings | Must contain at least one [supported algorithm](#supported-signing-algorithms) |
`authorization_endpoint` and `token_endpoint` are accepted but not used by Discord.
#### Supported Signing Algorithms
Your ID tokens must be signed using an asymmetric algorithm. Discord supports:
| Algorithm family | Algorithms |
| ---------------- | ------------------------- |
| RSA | `RS256`, `RS384`, `RS512` |
| ECDSA | `ES256`, `ES384`, `ES512` |
| RSA-PSS | `PS256`, `PS384`, `PS512` |
Symmetric (HMAC) algorithms such as `HS256` are not supported. Any unsupported algorithms listed in `id_token_signing_alg_values_supported` are silently ignored.
#### ID Token Requirements
The OIDC ID token passed as `external_auth_token` must meet all of the following:
| Requirement | Details |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `kid` header | The JWT header must include a `kid` (Key ID) field that matches a key in your JWKS |
| Signing algorithm | Must use one of the [supported algorithms](#supported-signing-algorithms) listed in your discovery document |
| `iss` claim | Must exactly match the configured issuer URL |
| `sub` claim | Required — the unique user identifier from your identity provider |
| `aud` claim | Must include the client ID configured for this application in the [Developer Portal](https://discord.com/developers/applications/select/social-sdk/external-auth-providers) |
| `exp` claim | Required — token must not be expired |
| `iat` claim | Required — token must have been issued within the past **7 days** |
The optional `preferred_username` claim (1–32 characters) sets the provisional account's display name if present.
### Provisional Account Access Tokens
These methods generate a Discord access token. You pass in the user's identity, and it generates a new Discord account tied to that identity. There are multiple ways of specifying that identity, including using Steam/Epic services or your own identity system.
All these methods will return with an access token that expires in 7 days.
#### Refreshing Provisional Account Tokens
Use [`Client::SetTokenExpirationCallback`] to receive a callback when the current token is about to expire or has expired, so you can refresh it without interrupting the user's experience.
When the token expires, re-call the same method you used originally to obtain a new access token, then pass it to [`Client::UpdateToken`].
When the token expires, the SDK will still receive updates, such as new messages sent in a lobby, and any voice calls will continue to be active. However, any new actions, such as sending a message or adding a friend, will fail. You can get a new token and pass it to [`Client::UpdateToken`] without interrupting the user's experience.
```cpp theme={"system"}
// Register a callback to handle token expiration
client->SetTokenExpirationCallback([client](discordpp::AuthorizationTokenType tokenType) {
// Re-acquire a new token using the same method you used originally.
// For example, if you used GetProvisionalToken:
std::string externalToken = GetExternalAuthToken(); // get a fresh token from your identity provider
client->GetProvisionalToken(DISCORD_APPLICATION_ID,
discordpp::AuthenticationExternalAuthType::OIDC,
externalToken,
[client](discordpp::ClientResult result, std::string accessToken, std::string refreshToken,
discordpp::AuthorizationTokenType tokenType, int32_t expiresIn, std::string scope) {
if (result.Successful()) {
// Pass the new access token to UpdateToken — no reconnect needed
client->UpdateToken(discordpp::AuthorizationTokenType::Bearer, accessToken, [](discordpp::ClientResult result) {
if (result.Successful()) {
std::cout << "✅ Token refreshed successfully\n";
}
});
} else {
std::cerr << "❌ Failed to refresh provisional token: " << result.Error() << std::endl;
}
});
});
```
If you are using Server Authentication with OIDC, a `refresh_token` is returned but using it via the OAuth2 `refresh_token` grant is **deprecated**. Re-authenticate using a fresh provider token instead.
#### Provisional Account Access Token Storage
It is suggested that these provisional tokens are not stored and instead invoke this function each time the game is launched and when these tokens are about to expire. However, should you choose to store it, it is recommended that these provisional account tokens be differentiated from "full" Discord account tokens.
### Error Handling
Common error codes and solutions for the server token exchange methods:
| Code | Meaning | Solution |
| ------ | ---------------------------- | --------------------------------------------------------------------------- |
| 530000 | Application not configured | Contact Discord support to enable provisional accounts for your application |
| 530001 | Expired ID token | Request a new token from your identity provider |
| 530004 | Token too old | Request a new token (tokens over 1 week old are rejected) |
| 530006 | Username generation failed | Retry the operation (temporary error) |
| 530007 | Invalid client secret | Verify or regenerate your client secret in the Developer Portal |
| 530010 | User account non-provisional | User already linked to Discord account - use standard OAuth2 flow |
If you are using OIDC, you may encounter more specific errors:
| Code | Meaning | Solution |
| ------ | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| 530002 | Invalid issuer | Verify the `iss` claim in your ID token exactly matches the issuer URL in your OIDC configuration |
| 530003 | Invalid audience | Verify the `aud` claim in your ID token includes the client ID in your OIDC configuration |
| 530008 | OIDC configuration not found | Verify your issuer URL is correct, accessible over HTTPS, and serves a valid discovery document without HTTP redirects |
| 530009 | OIDC JWKS not found | Verify your JWKS endpoint is accessible over HTTPS without HTTP redirects |
| 530020 | Invalid OIDC JWT token | Verify your ID token is properly signed and uses a supported algorithm |
| 530027 | Missing `kid` header | Ensure your ID token includes a `kid` (Key ID) header in the JWT header identifying the signing key |
***
## Setting Display Names
Using these credentials, we'll create a limited Discord account just for your game and try to set the username for you according to the following:
* For Bot issued tokens, the `preferred_global_name` you 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](https://partner.steamgames.com/doc/features/auth), the display name of the user's Steam
account is used as the provisional account's display name.
* For [EOS Auth](https://dev.epicgames.com/docs/epic-account-services/auth/auth-interface) 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 with
[`Client::UpdateProvisionalAccountDisplayName`].
* For [Unity Services ID Tokens](https://services.docs.unity.com/docs/client-auth/),
the display name of the user's Unity Player Account is used as the provisional account's display name.
If you'd like to set the display name for a provisional account, you can use the [`Client::UpdateProvisionalAccountDisplayName`] method.
```cpp theme={"system"}
client->UpdateProvisionalAccountDisplayName("CoolPlayer123", [](discordpp::ClientResult result) {
if (result.Successful()) {
std::cout << "✅ Display name updated\n";
}
}
);
```
***
## Merging Provisional Accounts
When a player wants to convert their provisional account to a full Discord account, we start a special version of the [access token request flow](/developers/discord-social-sdk/development-guides/account-linking-with-discord#requesting-access-tokens) where the provisional users external identity is included.
* If you have a backend, follow [Merging Provisional Accounts for Servers](/developers/discord-social-sdk/development-guides/using-provisional-accounts#merging-provisional-accounts-for-servers)
* If you do not have a backend, follow [Merging Provisional Accounts for Public Clients ](/developers/discord-social-sdk/development-guides/using-provisional-accounts#merging-provisional-accounts-for-public-clients)
Unlike other API rate limits, the merge operation has a strict per-user limit since account merging is not something we expect to happen frequently. Under normal circumstances, a player will only ever link their account once.
If you are testing your merge integration, make sure to add your QA users to your application's [App Testers](https://discord.com/developers/applications/select/testers) list to avoid hitting rate limits during testing.
### Merging Provisional Accounts for Servers
To merge a provisional account, extend the standard [OAuth2 token exchange](/developers/topics/oauth2#authorization-code-grant-access-token-exchange-example) by posting to `/oauth2/token` with two additional parameters — `external_auth_type` and `external_auth_token`. Discord uses these to identify the provisional account and merge it into the full Discord account associated with the provided authorization code or device code.
See the [External Auth Types](#external-auth-types) table above for the full list of supported `external_auth_type` values.
The `external_auth_token` is the same credential you provided when creating the provisional account — for example, your OIDC identity token, Steam session ticket, or EOS access token. If you created the provisional account using the [Bot Token Endpoint](#server-authentication-with-bot-token-endpoint), use `DISCORD_BOT_ISSUED_ACCESS_TOKEN` as the `external_auth_type` and the `access_token` returned by that endpoint (see [Bot Token Endpoint Response](#bot-token-endpoint-response)) as the `external_auth_token` — **not** the `external_user_id`.
The bot-issued `external_auth_token` is the **access token**, because the merge runs through the OAuth2 `/oauth2/token` endpoint and the token is what proves the external identity. This differs from the [bot unmerge endpoint](#unmerging-with-bot-token-endpoint), which is authenticated by your bot token and therefore identifies the account by `external_user_id` instead.
#### Desktop & Mobile
###### Request Body Parameters
| Parameter | Description |
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `grant_type` | Must be `authorization_code`. This is the standard [OAuth2 authorization code grant](/developers/topics/oauth2#authorization-code-grant-access-token-exchange-example) — the authorization code from the [`Client::Authorize`] flow is exchanged for an access token. |
| `code` | The authorization code returned to your server after the user completes the [`Client::Authorize`] flow. |
| `redirect_uri` | The redirect URI used in the original authorization request. Must match exactly. |
| `external_auth_type` | The type of external identity provider. See [External Auth Types](#external-auth-types). |
| `external_auth_token` | The external identity token. For example, for `OIDC`, this is the OIDC identity token. For `DISCORD_BOT_ISSUED_ACCESS_TOKEN`, this is the `access_token` returned by the Bot Token Endpoint (not the `external_user_id`). |
```python theme={"system"}
import requests
API_ENDPOINT = 'https://discord.com/api/v10'
CLIENT_ID = '332269999912132097'
CLIENT_SECRET = '937it3ow87i4ery69876wqire'
# See External Auth Types above for all supported values
EXTERNAL_AUTH_TYPE = 'DISCORD_BOT_ISSUED_ACCESS_TOKEN'
def exchange_code_with_merge(code, redirect_uri, external_auth_token):
data = {
'grant_type': 'authorization_code',
'code': code,
'redirect_uri': redirect_uri,
'external_auth_type': EXTERNAL_AUTH_TYPE,
'external_auth_token': external_auth_token
}
headers = {
'Content-Type': 'application/x-www-form-urlencoded'
}
r = requests.post('%s/oauth2/token' % API_ENDPOINT, data=data, headers=headers, auth=(CLIENT_ID, CLIENT_SECRET))
r.raise_for_status()
return r.json()
```
#### Console
###### Request Body Parameters
| Parameter | Description |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `grant_type` | Must be `urn:ietf:params:oauth:grant-type:device_code`. This is the [RFC 8628](https://www.rfc-editor.org/rfc/rfc8628) device authorization grant, used for consoles and devices without a browser. |
| `device_code` | The device code from the device authorization flow. See [Account Linking on Consoles](/developers/discord-social-sdk/development-guides/account-linking-on-consoles). |
| `external_auth_type` | The type of external identity provider. See [External Auth Types](#external-auth-types). |
| `external_auth_token` | The external identity token. For example, for `OIDC`, this is the OIDC identity token. For `DISCORD_BOT_ISSUED_ACCESS_TOKEN`, this is the `access_token` returned by the Bot Token Endpoint (not the `external_user_id`). |
```python theme={"system"}
import requests
API_ENDPOINT = 'https://discord.com/api/v10'
CLIENT_ID = '332269999912132097'
CLIENT_SECRET = '937it3ow87i4ery69876wqire'
# See External Auth Types above for all supported values
EXTERNAL_AUTH_TYPE = 'DISCORD_BOT_ISSUED_ACCESS_TOKEN'
def exchange_device_code_with_merge(device_code, external_auth_token):
data = {
'grant_type': 'urn:ietf:params:oauth:grant-type:device_code',
'device_code': device_code,
'external_auth_type': EXTERNAL_AUTH_TYPE,
'external_auth_token': external_auth_token
}
headers = {
'Content-Type': 'application/x-www-form-urlencoded'
}
r = requests.post('%s/oauth2/token' % API_ENDPOINT, data=data, headers=headers, auth=(CLIENT_ID, CLIENT_SECRET))
r.raise_for_status()
return r.json()
```
#### Merge Request Response
```json theme={"system"}
{
"access_token": "",
"token_type": "Bearer",
"expires_in": 604800,
"refresh_token": "",
"scope": "sdk.social_layer"
}
```
### Merging Provisional Accounts for Public Clients
This method requires enabling **Public Client** for your app. Most games will not want to ship with this enabled. [Learn more](/developers/discord-social-sdk/core-concepts/oauth2-scopes#oauth2-client-types)
If you do not have a backend, leverage the [`Client::GetTokenFromProvisionalMerge`] (Desktop & Mobile) or [`Client::GetTokenFromDeviceProvisionalMerge`] (Console) method, which will handle the entire process for you. You'll want to first enable Public Client on your Discord application's OAuth2 tab on the Discord developer portal. You can then leverage the [`Client::GetTokenFromProvisionalMerge`] or [`Client::GetTokenFromDeviceProvisionalMerge`] method using just the client.
This function should be used with the [`Client::Authorize`] function whenever a user with a provisional account wants to link to an existing Discord account or "upgrade" their provisional account into a "full" Discord account.
In this case, data from the provisional account should be "migrated" to the Discord account, a process we call "account merging". Specifically, relationships, DMs, and lobby memberships are transferred to the Discord account.
The provisional account will be deleted once this merging process is completed. If the user unlinks later, a new provisional account with a new unique ID is created.
The account merging process starts like the normal login flow, invoking the [`Client::Authorize`] method to get an authorization code back. Instead of calling `GetToken`, call this function and pass on the provisional user's identity.
Discord can then find the provisional account with that identity and the new Discord account and merge any data as necessary.
See the documentation for [`Client::GetToken`] for more details on the callback. Note that the callback will be invoked when the token exchange is complete, but merging accounts happens asynchronously and will not be complete yet.
```cpp theme={"system"}
// Create a code verifier and challenge if using GetToken
auto codeVerifier = client->CreateAuthorizationCodeVerifier();
discordpp::AuthorizationArgs args{};
args.SetClientId(YOUR_DISCORD_APPLICATION_ID);
args.SetScopes(discordpp::Client::GetDefaultPresenceScopes());
args.SetCodeChallenge(codeVerifier.Challenge());
client->Authorize(args, [client, codeVerifier](discordpp::ClientResult result, std::string code, std::string redirectUri) {
if (!result.Successful()) {
std::cerr << "❌ Authorization Error: " << result.Error() << std::endl;
} else {
std::cout << "✅ Authorization successful! Next step: GetTokenFromProvisionalMerge \n";
// Retrieve your external auth token
std::string externalAuthToken = GetExternalAuthToken();
client->GetTokenFromProvisionalMerge(YOUR_DISCORD_APPLICATION_ID, code, codeVerifier, redirectUri, discordpp::AuthenticationExternalAuthType::OIDC, externalAuthToken,[client](
discordpp::ClientResult result,
std::string accessToken,
std::string refreshToken,
discordpp::AuthorizationTokenType tokenType,
int32_t expiresIn,
std::string scope) {
if (result.Successful()) {
std::cout << "🔓 Token received! Establishing connection...\n";
client->UpdateToken(discordpp::AuthorizationTokenType::Bearer, accessToken, [client](discordpp::ClientResult result) {
client->Connect();
});
} else {
std::cerr << "❌ Token request failed: " << result.Error() << std::endl;
}
});
}
});
```
### Data Migration During Merging
When a user merges their provisional account with a Discord account, the following data is automatically transferred:
* **✅ Friends**: All In-game and Discord friendships made through the provisional account
* **✅ Lobby Memberships**: Active and historical lobby participation
* **✅ DM Messages**: Direct messages and history
This migration ensures users don't lose their social connections built while using the provisional account.
### Merge Request Failures
You may receive a merge specific error code while attempting this operation:
| Code | HTTP Status | Meaning | Solution |
| ------ | ----------- | ------------------------------- | --------------------------------------------------------------------- |
| 50025 | 403 | Invalid OAuth2 access token | The `external_auth_token` is invalid. |
| 530014 | 400 | Invalid merge source | The source account is not provisional |
| 530016 | 400 | Invalid merge destination | The destination account is provisional |
| 530017 | 400 | Merge source user banned | The provisional account being merged is banned from platform |
| 530023 | 400 | Too many application identities | User already has an associated external identity for this application |
| - | 423 | Resource locked | Transient error, wait and retry |
Error `530017` is most commonly seen *after* a successful link: when a previously linked Discord account is banned, that user's identity is severed back into a new provisional account in a **restricted** state, and that restricted provisional account cannot be merged into a different Discord account until the ban expires (temp ban) or is lifted. See [Ban-Driven Unmerge](#ban-driven-unmerge) for the full lifecycle.
***
## Unmerging Provisional Accounts
The link between a Discord account and a provisional account can be severed in four ways:
1. The user can unmerge their account from the Discord client
2. A developer can unmerge the account using the unmerge endpoint on the Discord API
3. A developer can use the SDK helper method for public clients
4. Discord can sever the link automatically when the user's Discord account is banned — see [Ban-Driven Unmerge](#ban-driven-unmerge) below
Unmerging invalidates all access/refresh tokens for the user. They cannot be used again after the unmerge operation completes. Any connected game sessions will be disconnected.
Unmerging does not affect the external identity you used to create the provisional account — your `external_auth_token`
(for OIDC, Steam, EOS, and other credential-exchange providers) or your `external_user_id` (for the
[Bot Token Endpoint](#server-authentication-with-bot-token-endpoint)). After the unmerge completes, you can
re-authenticate with that same identity to retrieve the token for the newly created provisional account.
### Unmerging Provisional Accounts for Servers
A developer can unmerge a user's account by sending a request to the unmerge endpoint on the Discord API.
```python theme={"system"}
import requests
API_ENDPOINT = 'https://discord.com/api/v10'
CLIENT_ID = '332269999912132097'
CLIENT_SECRET = '937it3ow87i4ery69876wqire'
EXTERNAL_AUTH_TYPE = 'OIDC'
def unmerge_provisional_account(external_auth_token):
data = {
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET,
'external_auth_type': EXTERNAL_AUTH_TYPE,
'external_auth_token': external_auth_token
}
r = requests.post('%s/partner-sdk/provisional-accounts/unmerge' % API_ENDPOINT, json=data, headers=headers)
r.raise_for_status()
```
If you have a server backend, you'll want to use the server-to-server unmerge endpoint rather than the SDK helper method to maintain better security and control over the unmerge process.
#### Unmerging with Bot Token Endpoint
If you're using the [Bot Token Endpoint](/developers/discord-social-sdk/development-guides/using-provisional-accounts#server-authentication-with-bot-token-endpoint) for authentication, you can unmerge accounts without an external auth token.
```python theme={"system"}
import requests
API_ENDPOINT = 'https://discord.com/api/v10'
BOT_TOKEN = 'YOUR_BOT_TOKEN'
def unmerge_provisional_account(external_user_id):
data = {
'external_user_id': external_user_id # identifier used in the /token/bot endpoint
}
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bot {BOT_TOKEN}'
}
r = requests.post('%s/partner-sdk/provisional-accounts/unmerge/bot' % API_ENDPOINT, json=data, headers=headers)
r.raise_for_status()
```
This endpoint can also be useful in cases where the Discord Auth token has been lost to error or data loss, and an unmerge operation is required to migrate to a provisional account before re-linking a Discord account.
### Unmerging Provisional Accounts for Public Clients
This method requires enabling **Public Client** for your app. Most games will not want to ship with this enabled. [Learn more](/developers/discord-social-sdk/core-concepts/oauth2-scopes#oauth2-client-types)
The quickest way to unmerge accounts is to leverage the [`Client::UnmergeIntoProvisionalAccount`] method,
which will handle the entire process for you. This method is designed for public clients that don't have a backend server.
**Important Notes:**
* This function only works for **public clients** (applications without backend servers)
* You'll need to enable "Public Client" on your Discord application's OAuth2 tab in the Discord developer portal
* After unmerging, you should use [`Client::GetProvisionalToken`] to get a new provisional token for the newly created provisional account
```cpp theme={"system"}
// unmerge a user account
void UnmergeUserAccount(const std::shared_ptr& client) {
// Get your external auth token (Steam, OIDC, etc.)
std::string externalToken = GetExternalAuthToken();
// Unmerge the Discord account from the external identity
client->UnmergeIntoProvisionalAccount(
YOUR_DISCORD_APPLICATION_ID,
discordpp::AuthenticationExternalAuthType::OIDC, // or STEAM, EOS, etc.
externalToken,
[client, externalToken](const discordpp::ClientResult &result) {
if (result.Successful()) {
std::cout << "✅ Account unmerged successfully! Creating new provisional account...\n";
// Now get a new provisional token for the unlinked identity
client->GetProvisionalToken(
YOUR_DISCORD_APPLICATION_ID,
discordpp::AuthenticationExternalAuthType::OIDC,
externalToken,
[client](const discordpp::ClientResult &result,
const std::string &accessToken,
const std::string& refreshToken,
discordpp::AuthorizationTokenType tokenType,
int32_t expiresIn,
const std::string& scopes) {
if (result.Successful()) {
std::cout << "🔓 New provisional account created! Establishing connection...\n";
client->UpdateToken(discordpp::AuthorizationTokenType::Bearer, accessToken,
[client](const discordpp::ClientResult &updateResult) {
if (updateResult.Successful()) {
client->Connect();
} else {
std::cerr << "❌ Failed to update token: " << updateResult.Error() << std::endl;
}
}
);
} else {
std::cerr << "❌ Failed to create new provisional account: " << result.Error() << std::endl;
}
}
);
} else {
std::cerr << "❌ Unmerge failed: " << result.Error() << std::endl;
}
}
);
}
```
### Out-of-Band Unmerge
The link can also be severed without your code calling any unmerge endpoint:
* **User-initiated**: the user removes your app from their Discord `User Settings -> Authorized Apps` page. The result is a standard unmerge — the user can re-link freely later.
* **Ban-driven**: when the user's Discord account is banned by Discord, the link is severed automatically. This is mechanically an unmerge, but with additional lifecycle consequences — see [Ban-Driven Unmerge](#ban-driven-unmerge) below.
In both cases your app observes the same auth-side signals — an `APPLICATION_DEAUTHORIZED` webhook fires and stored tokens are invalidated. See [Out-of-Band Revocation](/developers/discord-social-sdk/development-guides/account-linking-with-discord#out-of-band-revocation) in the Account Linking guide for the details and recommended recovery path.
These paths don't require any code changes from you, but we recommend providing an in-app unmerge option through one of the methods above for a better user experience.
#### Ban-Driven Unmerge
When a player's Discord account is banned (temporarily or permanently), Discord performs an unmerge automatically. Two things happen:
1. **OAuth2 tokens are deleted immediately** — producing the same `APPLICATION_DEAUTHORIZED` webhook and `invalid_grant`-on-refresh signals as any out-of-band revocation. There is no grace period.
2. **A new provisional account is created for the same external identity.** Standard [unmerge data migration](#data-migration-during-unmerging) applies — username, friends list, lobbies, and so on are preserved — so the player retains their in-game social graph even though their Discord identity is gone. The new provisional account is created in a **restricted** state.
While the new provisional account is restricted:
* It **cannot be merged** into a different Discord account. Any merge attempt via [`/oauth2/token` with `external_auth_token`](#merging-provisional-accounts-for-servers) will fail with error [`530017` "Merge source user is banned"](#merge-request-failures).
* For a **temporary ban**, the restriction lifts automatically when the ban expires, and the provisional account can then be merged again.
* For a **permanent ban**, the restriction stays in place indefinitely.
There is no API to look up whether a player's Discord account is currently banned. In practice, the combination of
`APPLICATION_DEAUTHORIZED` and/or `invalid_grant`-on-refresh is the signal you should fall
back to the [provisional account flow](#implementing-provisional-accounts).
### Data Migration During Unmerging
When a user unmerges their account, a new provisional account is created with a new user ID. The following data is transferred to the new provisional account:
* **✅ Username**: Global name is copied to the new provisional account
* **✅ In-game friends**: All copied to the new provisional account
* **✅ Discord friends who use this application**: Copied to the provisional account
* **✅ Blockers**: Accounts that blocked the original Discord account are preserved
* **✅ Lobbies**: Active lobby memberships for the application are transferred
The following data is **not** transferred:
* **❌ Discord friends who don't use this application**: Not transferred
* **❌ DM message history**: Not moved to provisional accounts
Provisional accounts can have Discord friends, but can only message these friends when actively playing the game.
### Unmerge Request Failures
You may receive an unmerge specific error code while attempting this operation:
| Code | HTTP Status | Meaning | Solution |
| ----- | ----------- | ----------------- | --------------------------------------------------------- |
| 50229 | 400 | Invalid user type | User account is provisional and cannot be unmerged |
| - | 404 | Unknown user | No user identity found for the provided external identity |
***
## Next Steps
Now that you've set up provisional accounts for your game, you can explore more features of the Discord Social SDK:
}>
Design guidelines for implementing provisional accounts in your game.
}>
Combine Discord and game friends into a single list for easy management.
}>
Display game status and information to Discord friends.
Need help? Join the [Discord Developers Server](https://discord.gg/discord-developers) and share questions in the `#social-sdk-dev-help` channel for support from the community.
If you encounter a bug while working with the Social SDK, please report it here: [https://dis.gd/social-sdk-bug-report](https://dis.gd/social-sdk-bug-report)
***
## Change Log
| Date | Changes |
| ----------------- | --------------------------------------------------------- |
| June 30, 2026 | Correct bot-issued `external_auth_token` for server merge |
| May 22, 2026 | Document ban-driven unmerge lifecycle |
| April 21, 2026 | Document OIDC integration requirements |
| April 14, 2026 | Clarify provisional account token refresh flow |
| April 13, 2026 | Clarify Merging Provisional Accounts for Servers |
| February 25, 2026 | Clarify unmerge behavior and data migration |
| March 17, 2025 | Initial release |
[`Client::Authorize`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ace94a58e27545a933d79db32b387a468
[`Client::GetProvisionalToken`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a8003130b6c46e54ac68442483bf0480c
[`Client::GetToken`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aaee636f91fb1ea3465157c20313b702c
[`Client::GetTokenFromDeviceProvisionalMerge`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#afd2207590ae7d6f60ee7bbb4fc7c21c8
[`Client::GetTokenFromProvisionalMerge`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a41062b7dafa331ddd2320daf1b4b273b
[`Client::SetTokenExpirationCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aab5bfc76809ea22e79f2f7a067ac4519
[`Client::UnmergeIntoProvisionalAccount`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a2da21ae8a3015e0e5e42c1a7226b256f
[`Client::UpdateProvisionalAccountDisplayName`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a7485979ab2d4c533b75f8efd5e50bc60
[`Client::UpdateToken`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a606b32cef7796f7fb91c2497bc31afc4
# Getting Started with the Discord Social SDK
Source: https://docs.discord.com/developers/discord-social-sdk/getting-started
Quick start guide for integrating the Discord Social SDK.
The Discord Social SDK allows you to integrate Discord social features into your game. With the Discord Social SDK, you can offer a seamless social experience for your players, allowing them to connect with their friends and communities on Discord without leaving your game.
If this is your first time learning about the Discord Social SDK, check out the Discord Social SDK [Overview](/developers/discord-social-sdk/overview) and [Core Concepts](/developers/discord-social-sdk/core-concepts) for more information on what the SDK can do and how it can benefit your game.
## Let's Get Started
Select a platform to get started.
If you are unsure which platform to choose, we recommend starting with the C++ guide to familiarize yourself with the SDK's core concepts.
For use with custom engines or standalone applications.
For use with Unity.
For use with Unreal Engine.
To see a complete list of currently compatible platforms, check out our [Platform Compatibility](/developers/discord-social-sdk/core-concepts/platform-compatibility) section.
***
## Next Steps
After you've run through a guide for your preferred platform, you can implement features such as a unified friend list, rich presence, and more. Check out the [Development Guides](/developers/discord-social-sdk/development-guides) for detailed information on using the SDK for each feature.
# Getting Started with C++ and the Discord Social SDK
Source: https://docs.discord.com/developers/discord-social-sdk/getting-started/using-c++
Get started with the Discord Social SDK in C++ applications.
## Overview
This guide will walk you through integrating the Discord Social SDK into a new standalone C++ project. By the end, you'll have an application that can:
* Authenticate users with Discord
* Set up logging and status monitoring
* Start the SDK and establish a connection
* Request the number of Discord friends the player has
* Set the player's rich presence for your game
### Prerequisites
We are going to make a simple C++ console application for this guide. Make sure you have the following prerequisites:
* Basic understanding of C++ and your platform's build system
* C++ Compiler and Build System that supports [C++20](https://en.cppreference.com/w/cpp/20) or greater.
* A network connection that can access Discord's API endpoints.
Let's walk through the steps in detail.
***
## Step 1: Create a Discord Developer Team
Before you start, you'll need to create a developer team on the Discord Developer Portal. This team will be used to manage your Discord applications and SDK integrations.
If you already have a team configured, you can skip this step.
1. Create a developer team on the [Discord Developer Portal](https://discord.com/developers/teams).
Later, you can invite your team members to your new team to collaborate on your integration.
***
## Step 2: Create a Discord Application
1. Create a new application on the [Discord Developer Portal](https://discord.com/developers/applications) and assign it to your team.
2. Add a redirect URL in the [OAuth2 tab](https://discord.com/developers/applications/select/oauth2):
* For desktop applications: `http://127.0.0.1/callback` (this can be changed later).
* See [`discordpp::Client::Authorize`](https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ace94a58e27545a933d79db32b387a468) for more details on setting up more advanced redirect URIs.
3. Enable the `Public Client` toggle in the [OAuth2 tab](https://discord.com/developers/applications/select/oauth2).
This guide requires enabling **Public Client** to allow you to get started with the SDK quickly. Most games will not want to ship as a public client. This setting should be reviewed by your team prior to releasing your game. [Learn more about public clients](/developers/discord-social-sdk/core-concepts/oauth2-scopes#oauth2-client-types).
***
## Step 3: Enable Discord Social SDK for Your App
Once you've created your Discord application, you'll need to enable the Discord Social SDK for it.
1. From the [Discord Developer Portal](https://discord.com/developers/applications), select your newly created application from Step 2.
2. In the left sidebar for your app, locate and click the [Getting Started](https://discord.com/developers/applications/select/social-sdk/getting-started) link under `Discord Social SDK`.
3. Fill out the form to share details about your game.
4. Click `Submit` and the Social SDK will be enabled for your application.
5. Once enabled, you'll find binaries for the Social SDK under [Downloads](https://discord.com/developers/applications/select/social-sdk/downloads).
***
## Step 4: Download the Discord SDK for C++
1. Click on the [Downloads](https://discord.com/developers/applications/select/social-sdk/downloads) link under the Discord Social SDK section of the sidebar.
2. Select the latest version from the version dropdown and download the SDK for C++.
### Runtime Dependencies
When shipping with the Social SDK, ensure you bundle the runtime dependencies:
* **Windows:** Ensure `discord_partner_sdk.dll` is in your executable directory.
* **Linux/macOS:** Make sure the `libdiscord_partner_sdk.so/libdiscord_partner_sdk.dylib` files are accessible via `LD_LIBRARY_PATH` or placed next to your binary.
***
## Step 5: Project Setup
To utilize the Discord Social SDK with C++, the following requirements must be met:
* `discordpp.h` is included in your C++ source code.
* The appropriate SDK libraries for your platform are linked in your build system:
* **Windows:** `discord_partner_sdk.dll`
* **Linux**: `libdiscord_partner_sdk.so`
* **macOS:** `libdiscord_partner_sdk.dylib`
All of which can be found in the SDK download archive.
💡 **Troubleshooting Tip:** If you encounter unresolved external symbols, ensure the SDK library is correctly linked in your build system.
Let's see this in action by starting with a folder for our project and the Social SDK dependency:
```bash theme={"system"}
mkdir MyGame
cd MyGame
mkdir lib
```
Unzip the Social SDK archive into the `lib` directory. You should end up with a `discord_social_sdk` folder under `lib` when complete.
Within the `MyGame` directory, create a `CMakeLists.txt` file:
```bash theme={"system"}
touch CMakeLists.txt
```
Add the following contents to the `CMakeLists.txt` file to set up the Social SDK dependency appropriately for your operating system:
```cmake theme={"system"}
cmake_minimum_required(VERSION 3.10)
project(DiscordSDKExample)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True)
add_executable(DiscordSDKExample main.cpp)
# Define some handy Social SDK variables
set(DISCORD_SDK_ROOT "${CMAKE_SOURCE_DIR}/lib/discord_social_sdk")
set(DISCORD_SDK_LIB_DIR "${DISCORD_SDK_ROOT}/lib/release")
set(DISCORD_SDK_BIN_DIR "${DISCORD_SDK_ROOT}/bin/release")
set(DISCORD_SDK_INCLUDE_DIR "${DISCORD_SDK_ROOT}/include")
# Include for Social SDK headers
target_include_directories(DiscordSDKExample PRIVATE ${DISCORD_SDK_INCLUDE_DIR})
# Platform-specific Social SDK library paths
if(WIN32)
set(DISCORD_LIB_PATH "${DISCORD_SDK_LIB_DIR}/discord_partner_sdk.lib")
set(DISCORD_SHARED_LIB "${DISCORD_SDK_BIN_DIR}/discord_partner_sdk.dll")
elseif(APPLE)
set(DISCORD_LIB_PATH "${DISCORD_SDK_LIB_DIR}/libdiscord_partner_sdk.dylib")
set(DISCORD_SHARED_LIB "${DISCORD_SDK_LIB_DIR}/libdiscord_partner_sdk.dylib")
else() # Linux
set(DISCORD_LIB_PATH "${DISCORD_SDK_LIB_DIR}/libdiscord_partner_sdk.so")
set(DISCORD_SHARED_LIB "${DISCORD_SDK_LIB_DIR}/libdiscord_partner_sdk.so")
endif()
# Link the Social SDK library
target_link_libraries(DiscordSDKExample PRIVATE ${DISCORD_LIB_PATH})
# Set the runtime search path (RPATH) for Linux and macOS
if(UNIX)
# Use RPATH when building
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
# Set the RPATH to use the lib directory relative to the executable
set(CMAKE_INSTALL_RPATH "$ORIGIN")
if(APPLE)
set(CMAKE_INSTALL_RPATH "@executable_path")
endif()
endif()
# Copy Social SDK shared library to output directory, so it's available at runtime.
add_custom_command(TARGET DiscordSDKExample POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${DISCORD_SHARED_LIB}"
$
)
```
Create a main.cpp file:
```bash theme={"system"}
touch main.cpp
```
Let's add the following code to `main.cpp`:
```cpp theme={"system"}
#define DISCORDPP_IMPLEMENTATION
#include "discordpp.h"
#include
#include
#include
#include
#include
#include
// Replace with your Discord Application ID
const uint64_t APPLICATION_ID = 123456789012345678;
// Create a flag to stop the application
std::atomic running = true;
// Signal handler to stop the application
void signalHandler(int signum) {
running.store(false);
}
int main() {
std::signal(SIGINT, signalHandler);
std::cout << "🚀 Initializing Discord SDK...\n";
// Create our Discord Client
auto client = std::make_shared();
// Keep application running to allow SDK to receive events and callbacks
while (running) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
return 0;
}
```
Compile and run the project:
```bash theme={"system"}
mkdir build && cd build
cmake ..
cmake --build .
./DiscordSDKExample
```
Although we've not done much yet other than create an instance of the client, you should see the following output printed to the console:
```
🚀 Initializing Discord SDK...
```
### Troubleshooting
On Mac you may get the error "libdiscord\_partner\_sdk.dylib" Not Opened because Apple couldn't verify it. If this happens press **Done** on the popup.
You'll need to open your **System Settings > Privacy & Security** and scroll down to the **Security** section. It will tell you "libdiscord\_partner\_sdk.dylib" was blocked to protect your Mac. Press **Open Anyway** and try running again.
Now when you get the pop up you'll have the option to select **Open Anyway** and it will be able to use it successfully.
***
## Step 6: Setting Up SDK Event Handling
Let's add some event handlers to monitor what's happening with our Discord connection. We'll set up two important callbacks:
1. A logging callback to see what the SDK is doing
2. A status callback to know when we can start using Discord features
### Adding Logging Support
First, let's add logging so we can see what's happening:
```cpp theme={"system"}
client->AddLogCallback([](auto message, auto severity) {
std::cout << "[" << EnumToString(severity) << "] " << message << std::endl;
}, discordpp::LoggingSeverity::Info);
```
### Monitoring Connection Status
Next, let's add a status callback that tells us when we're ready to use Discord features:
```cpp theme={"system"}
client->SetStatusChangedCallback([client](discordpp::Client::Status status, discordpp::Client::Error error, int32_t errorDetail) {
std::cout << "🔄 Status changed: " << discordpp::Client::StatusToString(status) << std::endl;
if (status == discordpp::Client::Status::Ready) {
std::cout << "✅ Client is ready! You can now call SDK functions.\n";
} else if (error != discordpp::Client::Error::None) {
std::cerr << "❌ Connection Error: " << discordpp::Client::ErrorToString(error) << " - Details: " << errorDetail << std::endl;
}
});
```
```cpp theme={"system"}
#define DISCORDPP_IMPLEMENTATION
#include "discordpp.h"
#include
#include
#include
#include
#include
#include
// Replace with your Discord Application ID
const uint64_t APPLICATION_ID = 123456789012345678;
// Create a flag to stop the application
std::atomic running = true;
// Signal handler to stop the application
void signalHandler(int signum) {
running.store(false);
}
int main() {
std::signal(SIGINT, signalHandler);
std::cout << "🚀 Initializing Discord SDK...\n";
// Create Discord Client
auto client = std::make_shared();
// Set up logging callback
client->AddLogCallback([](auto message, auto severity) {
std::cout << "[" << EnumToString(severity) << "] " << message << std::endl;
}, discordpp::LoggingSeverity::Info);
// Set up status callback to monitor client connection
client->SetStatusChangedCallback([client](discordpp::Client::Status status, discordpp::Client::Error error, int32_t errorDetail) {
std::cout << "🔄 Status changed: " << discordpp::Client::StatusToString(status) << std::endl;
if (status == discordpp::Client::Status::Ready) {
std::cout << "✅ Client is ready! You can now call SDK functions.\n";
} else if (error != discordpp::Client::Error::None) {
std::cerr << "❌ Connection Error: " << discordpp::Client::ErrorToString(error) << " - Details: " << errorDetail << std::endl;
}
});
// Keep application running to allow SDK to receive events and callbacks
while (running) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
return 0;
}
```
Most Discord features won't work until the status is \[`Client::Status::Ready`]. The status callback lets you know exactly when you can start using them.
### What These Callbacks Do
* The **logging callback** shows you what's happening behind the scenes
* The **status callback** tells you when you're connected and ready to use Discord features
At this point, these callbacks **won't get called** since the client setup is not yet complete. However, very soon we will be using them to view debug information and see what our connection status is!
To get to a \[`Client::Status::Ready`] state, we need to authenticate with Discord. We'll do that shortly.
***
## Step 7: Run Callbacks
Once you've registered callbacks with the SDK, you'll need to execute them in the event loop of your program. Add something like this to your game's main event loop or tick function.
Let's add [`RunCallbacks`] to our main loop:
```cpp theme={"system"}
// Keep application running to allow SDK to receive events and callbacks
while (running) {
discordpp::RunCallbacks();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
```
***
## Step 8: Account Linking with Discord
In this step, we'll implement OAuth2 authentication to support account-linking with Discord. This process will:
1. Open the Discord app or a browser window for Discord login
2. Get an authorization code
3. Exchange it for an access token
4. Connect to Discord
**Building for mobile?** Account linking on iOS and Android works differently. Jump to the [Account Linking on Mobile](/developers/discord-social-sdk/development-guides/account-linking-on-mobile) guide to set it up, then come back here to continue.
### Add the Authentication Code
Add this code to your `main.cpp` after setting up the status callback:
**Choosing your OAuth2 scopes:** This guide uses [`Client::GetDefaultPresenceScopes`], which requests the `openid` and `sdk.social_layer_presence` scopes. These enable core features like account linking, friends list, and rich presence.
If your game also needs lobbies, voice chat, or direct messaging, use [`Client::GetDefaultCommunicationScopes`] instead.
See the [OAuth2 Scopes guide](/developers/discord-social-sdk/core-concepts/oauth2-scopes) for the full breakdown.
```cpp theme={"system"}
// Generate OAuth2 code verifier for authentication
auto codeVerifier = client->CreateAuthorizationCodeVerifier();
// Set up authentication arguments
discordpp::AuthorizationArgs args{};
args.SetClientId(APPLICATION_ID);
args.SetScopes(discordpp::Client::GetDefaultPresenceScopes());
args.SetCodeChallenge(codeVerifier.Challenge());
// Begin authentication process
client->Authorize(args, [client, codeVerifier](auto result, auto code, auto redirectUri) {
if (!result.Successful()) {
std::cerr << "❌ Authentication Error: " << result.Error() << std::endl;
return;
} else {
std::cout << "✅ Authorization successful! Getting access token...\n";
// Exchange auth code for access token
client->GetToken(APPLICATION_ID, code, codeVerifier.Verifier(), redirectUri,
[client](discordpp::ClientResult result,
std::string accessToken,
std::string refreshToken,
discordpp::AuthorizationTokenType tokenType,
int32_t expiresIn,
std::string scope) {
std::cout << "🔓 Access token received! Establishing connection...\n";
// Next Step: Update the token and connect
});
}
});
```
### What's Happening Here?
1. We create a code verifier for OAuth2 PKCE security
2. Set up authorization arguments with your app ID and required scopes
3. Start the auth flow with [`Client::Authorize`], which opens a browser
4. When authorized, we exchange the code for an access token
Never log or store access tokens insecurely! They should be treated as sensitive credentials.
### Testing It Out
1. Build and run your program
2. A browser window should open asking you to authorize your app
3. After authorizing, watch the console for the "🔓 Access token received!" message
If you run into any issues:
* Double check your APPLICATION\_ID is correct
* Ensure you've added the redirect URL in your Discord Developer Portal
* Check the console for specific error messages
```cpp theme={"system"}
#define DISCORDPP_IMPLEMENTATION
#include "discordpp.h"
#include
#include
#include
#include
#include
#include
// Replace with your Discord Application ID
const uint64_t APPLICATION_ID = 123456789012345678;
// Create a flag to stop the application
std::atomic running = true;
// Signal handler to stop the application
void signalHandler(int signum) {
running.store(false);
}
int main() {
std::signal(SIGINT, signalHandler);
std::cout << "🚀 Initializing Discord SDK...\n";
// Create our Discord Client
auto client = std::make_shared();
// Set up logging callback
client->AddLogCallback([](auto message, auto severity) {
std::cout << "[" << EnumToString(severity) << "] " << message << std::endl;
}, discordpp::LoggingSeverity::Info);
// Set up status callback to monitor client connection
client->SetStatusChangedCallback([client](discordpp::Client::Status status, discordpp::Client::Error error, int32_t errorDetail) {
std::cout << "🔄 Status changed: " << discordpp::Client::StatusToString(status) << std::endl;
if (status == discordpp::Client::Status::Ready) {
std::cout << "✅ Client is ready! You can now call SDK functions.\n";
} else if (error != discordpp::Client::Error::None) {
std::cerr << "❌ Connection Error: " << discordpp::Client::ErrorToString(error) << " - Details: " << errorDetail << std::endl;
}
});
// Generate OAuth2 code verifier for authentication
auto codeVerifier = client->CreateAuthorizationCodeVerifier();
// Set up authentication arguments
discordpp::AuthorizationArgs args{};
args.SetClientId(APPLICATION_ID);
args.SetScopes(discordpp::Client::GetDefaultPresenceScopes());
args.SetCodeChallenge(codeVerifier.Challenge());
// Begin authentication process
client->Authorize(args, [client, codeVerifier](auto result, auto code, auto redirectUri) {
if (!result.Successful()) {
std::cerr << "❌ Authentication Error: " << result.Error() << std::endl;
return;
} else {
std::cout << "✅ Authorization successful! Getting access token...\n";
// Exchange auth code for access token
client->GetToken(APPLICATION_ID, code, codeVerifier.Verifier(), redirectUri,
[client](discordpp::ClientResult result,
std::string accessToken,
std::string refreshToken,
discordpp::AuthorizationTokenType tokenType,
int32_t expiresIn,
std::string scope) {
std::cout << "🔓 Access token received! Establishing connection...\n";
// Next Step: Update the token and connect
});
}
});
// Keep application running to allow SDK to receive events and callbacks
while (running) {
discordpp::RunCallbacks();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
return 0;
}
```
***
## Step 9: Connect the SDK to Discord
Now that we have our access token, let's connect to Discord! This involves two steps:
1. Updating the SDK with our access token with [`Client::UpdateToken`].
2. Establishing the connection with [`Client::Connect`].
Find the comment `// Next Step: Update the token and connect` within `client->UpdateToken()`, and add the
following code after:
```cpp theme={"system"}
// Next Step: Update the token and connect
client->UpdateToken(discordpp::AuthorizationTokenType::Bearer, accessToken, [client](discordpp::ClientResult result) {
if(result.Successful()) {
std::cout << "🔑 Token updated, connecting to Discord...\n";
client->Connect();
}
});
```
### What's Happening Here?
1. `client->UpdateToken()` tells the SDK to use our access token for Discord API calls
2. Once the token is updated, we call `client->Connect()` in the callback
3. The SDK will begin connecting asynchronously
4. Our status callback (from Step 6) will tell us when we're ready
Watch your console output! You should see status updates as the connection is established.
### Testing the Connection
1. Run your program
2. Watch for these status messages in order:
* "🔑 Token updated, connecting to Discord..."
* "🔄 Status changed: Connecting" (also "Connected" and "Ready")
* "✅ Client is ready! You can now call SDK functions."
### Troubleshooting
If you don't see "Ready" status:
* Check that your access token is valid
* Ensure you have internet connectivity
* Look for error messages in the status callback
* Verify your `APPLICATION_ID` is correct
Now that your client is in a ready state, we can start implementing Discord social features.
```cpp theme={"system"}
#define DISCORDPP_IMPLEMENTATION
#include "discordpp.h"
#include
#include
#include
#include
#include
#include
// Replace with your Discord Application ID
const uint64_t APPLICATION_ID = 1349146942634065960;
// Create a flag to stop the application
std::atomic running = true;
// Signal handler to stop the application
void signalHandler(int signum) {
running.store(false);
}
int main() {
std::signal(SIGINT, signalHandler);
std::cout << "🚀 Initializing Discord SDK...\n";
// Create our Discord Client
auto client = std::make_shared();
// Set up logging callback
client->AddLogCallback([](auto message, auto severity) {
std::cout << "[" << EnumToString(severity) << "] " << message << std::endl;
}, discordpp::LoggingSeverity::Info);
// Set up status callback to monitor client connection
client->SetStatusChangedCallback([client](discordpp::Client::Status status, discordpp::Client::Error error, int32_t errorDetail) {
std::cout << "🔄 Status changed: " << discordpp::Client::StatusToString(status) << std::endl;
if (status == discordpp::Client::Status::Ready) {
std::cout << "✅ Client is ready! You can now call SDK functions.\n";
} else if (error != discordpp::Client::Error::None) {
std::cerr << "❌ Connection Error: " << discordpp::Client::ErrorToString(error) << " - Details: " << errorDetail << std::endl;
}
});
// Generate OAuth2 code verifier for authentication
auto codeVerifier = client->CreateAuthorizationCodeVerifier();
// Set up authentication arguments
discordpp::AuthorizationArgs args{};
args.SetClientId(APPLICATION_ID);
args.SetScopes(discordpp::Client::GetDefaultPresenceScopes());
args.SetCodeChallenge(codeVerifier.Challenge());
// Begin authentication process
client->Authorize(args, [client, codeVerifier](auto result, auto code, auto redirectUri) {
if (!result.Successful()) {
std::cerr << "❌ Authentication Error: " << result.Error() << std::endl;
return;
} else {
std::cout << "✅ Authorization successful! Getting access token...\n";
// Exchange auth code for access token
client->GetToken(APPLICATION_ID, code, codeVerifier.Verifier(), redirectUri,
[client](discordpp::ClientResult result,
std::string accessToken,
std::string refreshToken,
discordpp::AuthorizationTokenType tokenType,
int32_t expiresIn,
std::string scope) {
std::cout << "🔓 Access token received! Establishing connection...\n";
// Next Step: Update the token and connect
client->UpdateToken(discordpp::AuthorizationTokenType::Bearer, accessToken, [client](discordpp::ClientResult result) {
if(result.Successful()) {
std::cout << "🔑 Token updated, connecting to Discord...\n";
client->Connect();
}
});
});
}
});
// Keep application running to allow SDK to receive events and callbacks
while (running) {
discordpp::RunCallbacks();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
return 0;
}
```
***
## Step 10: Access Discord Relationships
Let's access the user's Discord relationships (friends list) and display the count. This will help you understand how to access and use Discord data in your game.
Within `client->SetStatusChangedCallback()`, add the following after `status == discordpp::Client::Status::Ready`
code to view how many friends you have in Discord:
```cpp theme={"system"}
// Access initial relationships data
std::cout << "👥 Friends Count: " << client->GetRelationships().size() << std::endl;
```
### What This Code Does
2. [`Client::GetRelationships`] gives you immediate access to the current friend list
### Example Output
```
👥 Friends Count: 42
```
### Testing It Out
1. Run your program
2. Wait for the initial friend count
This relationship data forms the foundation for features like friend lists, activity feeds, and multiplayer invites!
### Troubleshooting
If you're not seeing relationship data:
* Verify your OAuth2 scopes include relationships access
* Ensure you're connected (Status::Ready)
* Check that you have friends on Discord
* Look for errors in the logging callback
Next, we'll learn how to show your game's activity on Discord with Rich Presence!
```cpp theme={"system"}
#define DISCORDPP_IMPLEMENTATION
#include "discordpp.h"
#include
#include
#include
#include
#include
#include
// Replace with your Discord Application ID
const uint64_t APPLICATION_ID = 1349146942634065960;
// Create a flag to stop the application
std::atomic running = true;
// Signal handler to stop the application
void signalHandler(int signum) {
running.store(false);
}
int main() {
std::signal(SIGINT, signalHandler);
std::cout << "🚀 Initializing Discord SDK...\n";
// Create our Discord Client
auto client = std::make_shared();
// Set up logging callback
client->AddLogCallback([](auto message, auto severity) {
std::cout << "[" << EnumToString(severity) << "] " << message << std::endl;
}, discordpp::LoggingSeverity::Info);
// Set up status callback to monitor client connection
client->SetStatusChangedCallback([client](discordpp::Client::Status status, discordpp::Client::Error error, int32_t errorDetail) {
std::cout << "🔄 Status changed: " << discordpp::Client::StatusToString(status) << std::endl;
if (status == discordpp::Client::Status::Ready) {
std::cout << "✅ Client is ready! You can now call SDK functions.\n";
// Access initial relationships data
std::cout << "👥 Friends Count: " << client->GetRelationships().size() << std::endl;
} else if (error != discordpp::Client::Error::None) {
std::cerr << "❌ Connection Error: " << discordpp::Client::ErrorToString(error) << " - Details: " << errorDetail << std::endl;
}
});
// Generate OAuth2 code verifier for authentication
auto codeVerifier = client->CreateAuthorizationCodeVerifier();
// Set up authentication arguments
discordpp::AuthorizationArgs args{};
args.SetClientId(APPLICATION_ID);
args.SetScopes(discordpp::Client::GetDefaultPresenceScopes());
args.SetCodeChallenge(codeVerifier.Challenge());
// Begin authentication process
client->Authorize(args, [client, codeVerifier](auto result, auto code, auto redirectUri) {
if (!result.Successful()) {
std::cerr << "❌ Authentication Error: " << result.Error() << std::endl;
return;
} else {
std::cout << "✅ Authorization successful! Getting access token...\n";
// Exchange auth code for access token
client->GetToken(APPLICATION_ID, code, codeVerifier.Verifier(), redirectUri,
[client](discordpp::ClientResult result,
std::string accessToken,
std::string refreshToken,
discordpp::AuthorizationTokenType tokenType,
int32_t expiresIn,
std::string scope) {
std::cout << "🔓 Access token received! Establishing connection...\n";
// Next Step: Update the token and connect
client->UpdateToken(discordpp::AuthorizationTokenType::Bearer, accessToken, [client](discordpp::ClientResult result) {
if(result.Successful()) {
std::cout << "🔑 Token updated, connecting to Discord...\n";
client->Connect();
}
});
});
}
});
// Keep application running to allow SDK to receive events and callbacks
while (running) {
discordpp::RunCallbacks();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
return 0;
}
```
***
## Step 11: Set Rich Presence
Let's show your game's activity on Discord using Rich Presence. This feature lets players see what others are doing in your game directly in their Discord friends list.
### Add Rich Presence Code
Right after the line where we called `client->GetRelationships()` let's add the following code to set the your rich
presence:
```cpp theme={"system"}
// Configure rich presence details
discordpp::Activity activity;
activity.SetType(discordpp::ActivityTypes::Playing);
activity.SetState("In Competitive Match");
activity.SetDetails("Rank: Diamond II");
// Update rich presence
client->UpdateRichPresence(activity, [](discordpp::ClientResult result) {
if(result.Successful()) {
std::cout << "🎮 Rich Presence updated successfully!\n";
} else {
std::cerr << "❌ Rich Presence update failed";
}
});
```
### What This Code Does
1. Creates an [`Activity`] object to represent what the player is doing
2. Sets basic information like:
* The activity type (Playing)
* Current state ("In Competitive Match")
* Additional details ("Rank: Diamond II")
3. Updates your rich presence on Discord
### Testing It Out
1. Run your program
2. Watch for the console message "🎮 Rich Presence updated successfully!"
3. Check your Discord profile, you should see:
* "Playing \[Your Game]"
* "In Competitive Match"
* "Rank: Diamond II"
### Troubleshooting
If you don't see your presence:
* Ensure you're connected (\[`Client::Status::Ready`])
* Check the callback for error messages
* Verify your activity settings are valid
* Make sure you're not invisible on Discord
```cpp theme={"system"}
#define DISCORDPP_IMPLEMENTATION
#include "discordpp.h"
#include
#include
#include
#include
#include
#include
// Replace with your Discord Application ID
const uint64_t APPLICATION_ID = 1349146942634065960;
// Create a flag to stop the application
std::atomic running = true;
// Signal handler to stop the application
void signalHandler(int signum) {
running.store(false);
}
int main() {
std::signal(SIGINT, signalHandler);
std::cout << "🚀 Initializing Discord SDK...\n";
// Create our Discord Client
auto client = std::make_shared();
// Set up logging callback
client->AddLogCallback([](auto message, auto severity) {
std::cout << "[" << EnumToString(severity) << "] " << message << std::endl;
}, discordpp::LoggingSeverity::Info);
// Set up status callback to monitor client connection
client->SetStatusChangedCallback([client](discordpp::Client::Status status, discordpp::Client::Error error, int32_t errorDetail) {
std::cout << "🔄 Status changed: " << discordpp::Client::StatusToString(status) << std::endl;
if (status == discordpp::Client::Status::Ready) {
std::cout << "✅ Client is ready! You can now call SDK functions.\n";
// Access initial relationships data
std::cout << "👥 Friends Count: " << client->GetRelationships().size() << std::endl;
// Configure rich presence details
discordpp::Activity activity;
activity.SetType(discordpp::ActivityTypes::Playing);
activity.SetState("In Competitive Match");
activity.SetDetails("Rank: Diamond II");
// Update rich presence
client->UpdateRichPresence(activity, [](discordpp::ClientResult result) {
if(result.Successful()) {
std::cout << "🎮 Rich Presence updated successfully!\n";
} else {
std::cerr << "❌ Rich Presence update failed";
}
});
} else if (error != discordpp::Client::Error::None) {
std::cerr << "❌ Connection Error: " << discordpp::Client::ErrorToString(error) << " - Details: " << errorDetail << std::endl;
}
});
// Generate OAuth2 code verifier for authentication
auto codeVerifier = client->CreateAuthorizationCodeVerifier();
// Set up authentication arguments
discordpp::AuthorizationArgs args{};
args.SetClientId(APPLICATION_ID);
args.SetScopes(discordpp::Client::GetDefaultPresenceScopes());
args.SetCodeChallenge(codeVerifier.Challenge());
// Begin authentication process
client->Authorize(args, [client, codeVerifier](auto result, auto code, auto redirectUri) {
if (!result.Successful()) {
std::cerr << "❌ Authentication Error: " << result.Error() << std::endl;
return;
} else {
std::cout << "✅ Authorization successful! Getting access token...\n";
// Exchange auth code for access token
client->GetToken(APPLICATION_ID, code, codeVerifier.Verifier(), redirectUri,
[client](discordpp::ClientResult result,
std::string accessToken,
std::string refreshToken,
discordpp::AuthorizationTokenType tokenType,
int32_t expiresIn,
std::string scope) {
std::cout << "🔓 Access token received! Establishing connection...\n";
// Next Step: Update the token and connect
client->UpdateToken(discordpp::AuthorizationTokenType::Bearer, accessToken, [client](discordpp::ClientResult result) {
if(result.Successful()) {
std::cout << "🔑 Token updated, connecting to Discord...\n";
client->Connect();
}
});
});
}
});
// Keep application running to allow SDK to receive events and callbacks
while (running) {
discordpp::RunCallbacks();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
return 0;
}
```
***
## Conclusion
Congratulations! You've successfully integrated the Discord Social SDK into your C++ application. Let's review what you've accomplished:
### What You've Built
* ✅ Created a Discord application and configured OAuth2
* ✅ Set up SDK logging and status monitoring
* ✅ Implemented user authentication flow
* ✅ Retrieved Discord relationships data
* ✅ Added Rich Presence support
### Key Concepts Learned
* How to initialize and configure the Discord SDK
* Managing authentication and connections
* Working with Discord's social features
* Handling asynchronous callbacks
* Monitoring SDK status and events
***
## Next Steps
You have successfully set up the Discord Social SDK with C++ and authenticated with Discord! You can now use the SDK to add more social features in your project.
}>
Create a unified friends list combining Discord and game-specific friendships
}>
Customize your game's rich presence to show more advanced information and game invites
}>
Allow players to invite friends to join their game session or party.
Need help? Join the [Discord Developers Server](https://discord.gg/discord-developers) and share questions in the `#social-sdk-dev-help` channel for support from the community.
If you encounter a bug while working with the Social SDK, please report it here: [https://dis.gd/social-sdk-bug-report](https://dis.gd/social-sdk-bug-report)
***
## Change Log
| Date | Changes |
| -------------- | ------------------------------------------------------- |
| May 20, 2026 | Updated required C++ standard to C++20 with 1.9 release |
| March 17, 2025 | initial release |
[`Activity`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#ae793d9adbe16fef402b859ba02bee682
[`Client::Authorize`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ace94a58e27545a933d79db32b387a468
[`Client::Connect`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a873a844c7c4c72e9e693419bb3e290aa
[`Client::GetDefaultCommunicationScopes`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a71499da752fbdc2d4326ae0fd36c0dd1
[`Client::GetDefaultPresenceScopes`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a7648bd1d2f7d9a86ebd0edb8bef12b5c
[`Client::GetRelationships`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ad481849835cd570f0e03adafcf90125d
[`Client::UpdateToken`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a606b32cef7796f7fb91c2497bc31afc4
[`RunCallbacks`]: https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#ab5dd8cf274f581ee1885de5816be3c29
# Getting Started with Unity and the Discord Social SDK
Source: https://docs.discord.com/developers/discord-social-sdk/getting-started/using-unity
Integrate the Discord Social SDK into Unity games.
## Overview
This guide will walk you through integrating the Discord Social SDK into a Unity project. By the end, you'll have a project that can:
* Authenticate users with Discord
* Set up logging and status monitoring
* Start the SDK and establish a connection
* Request the number of Discord friends the player has
* Set the player's rich presence for your game
### Prerequisites
Before starting, ensure you have:
* Unity 2021.3 or later
Let's walk through the steps in detail.
***
## Step 1: Create a Discord Developer Team
Before you start, you'll need to create a developer team on the Discord Developer Portal. This team will be used to manage your Discord applications and SDK integrations.
If you already have a team configured, you can skip this step.
1. Create a developer team on the [Discord Developer Portal](https://discord.com/developers/teams).
Later, you can invite your team members to your new team to collaborate on your integration.
***
## Step 2: Create a Discord Application
1. Create a new application on the [Discord Developer Portal](https://discord.com/developers/applications) and assign it to your team.
2. Add a redirect URL in the [OAuth2 tab](https://discord.com/developers/applications/select/oauth2):
* For desktop applications: `http://127.0.0.1/callback` (this can be changed later).
* See [`discordpp::Client::Authorize`](https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ace94a58e27545a933d79db32b387a468) for more details on setting up more advanced redirect URIs.
3. Enable the `Public Client` toggle in the [OAuth2 tab](https://discord.com/developers/applications/select/oauth2).
This guide requires enabling **Public Client** to allow you to get started with the SDK quickly. Most games will not want to ship as a public client. This setting should be reviewed by your team prior to releasing your game. [Learn more about public clients](/developers/discord-social-sdk/core-concepts/oauth2-scopes#oauth2-client-types).
***
## Step 3: Enable Discord Social SDK for Your App
Once you've created your Discord application, you'll need to enable the Discord Social SDK for it.
1. From the [Discord Developer Portal](https://discord.com/developers/applications), select your newly created application from Step 2.
2. In the left sidebar for your app, locate and click the [Getting Started](https://discord.com/developers/applications/select/social-sdk/getting-started) link under `Discord Social SDK`.
3. Fill out the form to share details about your game.
4. Click `Submit` and the Social SDK will be enabled for your application.
5. Once enabled, you'll find binaries for the Social SDK under [Downloads](https://discord.com/developers/applications/select/social-sdk/downloads).
***
## Step 4: Download the Social SDK for Unity
1. Click on the [Downloads](https://discord.com/developers/applications/select/social-sdk/downloads) link under the Discord Social SDK section of the sidebar.
2. Select the latest version from the version dropdown and download the SDK for Unity.
A Unity sample project is available for download on this page, but we are not going to cover it in this guide. Explore it on your own after you finish this guide!
***
## Step 5: Project Setup
Let's set up your Unity project to include the Social SDK package and add the necessary objects and scripts to use it.
1. Create a new 2D project in Unity Hub using Unity version 2021.3 or later
2. Either:
1. Unzip the zip file in the Unity `Packages` folder, or
2. Unzip the zip file and [Install Package from Disk](https://docs.unity3d.com/Manual/upm-ui-local.html). Make sure the folder is in a directory that won't get moved or deleted as your Unity project will load it from that location.
3. In your project add a `Scripts` folder and create a `DiscordManager.cs` script
4. Add the following code to `DiscordManager.cs`:
```cs theme={"system"}
using UnityEngine;
using UnityEngine.UI;
using Discord.Sdk;
using System.Linq;
public class DiscordManager : MonoBehaviour
{
[SerializeField]
private ulong clientId; // Set this in the Unity Inspector from the dev portal
[SerializeField]
private Button loginButton;
[SerializeField]
private Text statusText;
private Client client;
private string codeVerifier;
}
```
6. Add an empty object to the scene (**GameObject > Create Empty**) called **DiscordManager** and attach the `DiscordManager.cs` script to it
7. Add a button to the scene **GameObject > UI > Legacy > Button**
8. Add text to the scene **GameObject > UI > Legacy > Text**
9. Position the button and text somewhere visible on the screen
10. Attach the button and text to the **DiscordManager** in the inspector
11. Run it!
This is all we'll need to get started! You shouldn't see anything happen, but if you run into any issues, check out the troubleshooting section before moving to the next step.
### Troubleshooting
* Make sure the Social SDK package was successfully added to Unity
On Mac you may get the error "libdiscord\_partner\_sdk.dylib" Not Opened because Apple couldn't verify it. If this happens press **Done** on the popup.
You'll need to open your **System Settings > Privacy & Security** and scroll down to the **Security** section. It will tell you "libdiscord\_partner\_sdk.dylib" was blocked to protect your Mac. Press **Open Anyway** and try running again.
Now when you get the pop up you'll have the option to select **Open Anyway** and it will be able to use it successfully.
***
## Step 6: Setting Up SDK Event Handling
Let's add some event handlers to monitor what's happening with our Discord connection. We'll set up two important callbacks:
* A logging callback to see what the SDK is doing
* A status callback to know when we can start using Discord features
We'll start by adding the following code to your `DiscordManager.cs`:
```cs theme={"system"}
void Start()
{
client = new Client();
// Modifying LoggingSeverity will show you more or less logging information
client.AddLogCallback(OnLog, LoggingSeverity.Error);
client.SetStatusChangedCallback(OnStatusChanged);
// Make sure the button has a listener
if (loginButton != null)
{
//loginButton.onClick.AddListener(StartOAuthFlow);
}
else
{
Debug.LogError("Login button reference is missing, connect it in the inspector!");
}
// Set initial status text
if (statusText != null)
{
statusText.text = "Ready to login";
}
else
{
Debug.LogError("Status text reference is missing, connect it in the inspector!");
}
}
private void OnLog(string message, LoggingSeverity severity)
{
Debug.Log($"Log: {severity} - {message}");
}
private void OnStatusChanged(Client.Status status, Client.Error error, int errorCode)
{
Debug.Log($"Status changed: {status}");
statusText.text = status.ToString();
if(error != Client.Error.None)
{
Debug.LogError($"Error: {error}, code: {errorCode}");
}
}
```
This will hook up the API status changes to your text in the game. Then we'll need to get your Client ID from the [OAuth2 tab](https://discord.com/developers/applications/select/oauth2) in the developer portal and paste it into the ClientID on the **DiscordManager** in the inspector.
### What These Callbacks Do
* The **logging callback** shows you what's happening behind the scenes and is a powerful tool for debugging
* The **status callback** tells you when you're connected and ready to use Discord features
The Unity plugin handles running the SDK callbacks for you in Unity, no need to use [`RunCallbacks`] like we do in the [C++ guide](/developers/discord-social-sdk/getting-started/using-c++).
Most Discord features won't work until the status is `Ready`. The status callback lets you know when you can start using them.
To get to a `Ready` state, we need to authenticate with Discord. Let's do that next.
***
## Step 7: Account Linking with Discord
In this step, we'll implement OAuth2 authentication to support account linking with Discord. This process will:
1. Open the Discord app or a browser window for Discord login
2. Get an authorization code
3. Exchange it for an access token
4. Connect to Discord
**Building for mobile?** Account linking on iOS and Android works differently. Jump to the [Account Linking on Mobile](/developers/discord-social-sdk/development-guides/account-linking-on-mobile) guide to set it up, then come back here to continue.
### Add the Authentication Code
**Choosing your OAuth2 scopes:** This guide uses `Client.GetDefaultPresenceScopes()`, which requests the `openid` and `sdk.social_layer_presence` scopes. These enable core features like account linking, friends list, and rich presence.
If your game also needs lobbies, voice chat, or direct messaging, use `Client.GetDefaultCommunicationScopes()` instead.
See the [OAuth2 Scopes guide](/developers/discord-social-sdk/core-concepts/oauth2-scopes) for the full breakdown.
To start we'll add this code to your `DiscordManager.cs`:
```cs theme={"system"}
private void StartOAuthFlow() {
var authorizationVerifier = client.CreateAuthorizationCodeVerifier();
codeVerifier = authorizationVerifier.Verifier();
var args = new AuthorizationArgs();
args.SetClientId(clientId);
args.SetScopes(Client.GetDefaultPresenceScopes());
args.SetCodeChallenge(authorizationVerifier.Challenge());
client.Authorize(args, OnAuthorizeResult);
}
private void OnAuthorizeResult(ClientResult result, string code, string redirectUri) {
Debug.Log($"Authorization result: [{result.Error()}] [{code}] [{redirectUri}]");
if (!result.Successful()) {
return;
}
GetTokenFromCode(code, redirectUri);
}
private void GetTokenFromCode(string code, string redirectUri) {
client.GetToken(clientId,
code,
codeVerifier,
redirectUri,
(result, token, refreshToken, tokenType, expiresIn, scope) => {});
}
```
and then we'll uncomment `loginButton.onClick.AddListener(StartOAuthFlow);` in your `Start()` method
```cs theme={"system"}
if (loginButton != null)
{
loginButton.onClick.AddListener(StartOAuthFlow);
}
```
### What's Happening Here?
1. We create a code verifier for OAuth2 PKCE security
2. Set up authorization arguments with your app ID and required scopes
3. Start the auth flow with [`Client::Authorize`], which opens a browser
4. When authorized, we exchange the code for an access token
Never log or store access tokens insecurely! They should be treated as sensitive credentials.
### Testing It Out
Now, if you press play and click the button, it should start the OAuth flow! You'll be redirected to your browser to log in and authorize the game. There will be some logging to the console, but the status won't change yet.
### Troubleshooting
* Make sure you've uncommented `loginButton.onClick.AddListener(StartOAuthFlow);` if the button doesn't seem to do anything
* Double check your `ClientId` is correct
* Ensure you've added the redirect URL in your Discord Developer Portal
* Check the console for specific error messages
```cs theme={"system"}
using UnityEngine;
using UnityEngine.UI;
using Discord.Sdk;
using System.Linq;
public class DiscordManager : MonoBehaviour
{
[SerializeField]
private ulong clientId; // Set this in the Unity Inspector from the dev portal
[SerializeField]
private Button loginButton;
[SerializeField]
private Text statusText;
private Client client;
private string codeVerifier;
void Start()
{
client = new Client();
// Modifying LoggingSeverity will show you more or less logging information
client.AddLogCallback(OnLog, LoggingSeverity.Error);
client.SetStatusChangedCallback(OnStatusChanged);
// Make sure the button has a listener
if (loginButton != null)
{
loginButton.onClick.AddListener(StartOAuthFlow);
}
else
{
Debug.LogError("Login button reference is missing, connect it in the inspector!");
}
// Set initial status text
if (statusText != null)
{
statusText.text = "Ready to login";
}
else
{
Debug.LogError("Status text reference is missing, connect it in the inspector!");
}
}
private void OnLog(string message, LoggingSeverity severity)
{
Debug.Log($"Log: {severity} - {message}");
}
private void OnStatusChanged(Client.Status status, Client.Error error, int errorCode)
{
Debug.Log($"Status changed: {status}");
statusText.text = status.ToString();
if(error != Client.Error.None)
{
Debug.LogError($"Error: {error}, code: {errorCode}");
}
}
private void StartOAuthFlow() {
var authorizationVerifier = client.CreateAuthorizationCodeVerifier();
codeVerifier = authorizationVerifier.Verifier();
var args = new AuthorizationArgs();
args.SetClientId(clientId);
args.SetScopes(Client.GetDefaultPresenceScopes());
args.SetCodeChallenge(authorizationVerifier.Challenge());
client.Authorize(args, OnAuthorizeResult);
}
private void OnAuthorizeResult(ClientResult result, string code, string redirectUri) {
Debug.Log($"Authorization result: [{result.Error()}] [{code}] [{redirectUri}]");
if (!result.Successful()) {
return;
}
GetTokenFromCode(code, redirectUri);
}
private void GetTokenFromCode(string code, string redirectUri) {
client.GetToken(clientId,
code,
codeVerifier,
redirectUri,
(result, token, refreshToken, tokenType, expiresIn, scope) => {});
}
}
```
***
## Step 8: Connect the SDK to Discord
Now that we have our access token, let's connect to Discord!
To start, we'll add this code to your `DiscordManager.cs`:
```cs theme={"system"}
private void OnReceivedToken(string token) {
Debug.Log("Token received: " + token);
client.UpdateToken(AuthorizationTokenType.Bearer, token, (ClientResult result) => { client.Connect(); });
}
private void OnRetrieveTokenFailed() { statusText.text = "Failed to retrieve token"; }
```
Then we'll update `GetTokenFromCode` to call these functions when it completes:
```cs theme={"system"}
private void GetTokenFromCode(string code, string redirectUri) {
client.GetToken(clientId,
code,
codeVerifier,
redirectUri,
(result, token, refreshToken, tokenType, expiresIn, scope) => {
if (token != "") {
OnReceivedToken(token);
} else {
OnRetrieveTokenFailed();
}
});
}
```
### What's Happening Here?
1. [`Client::UpdateToken`] tells the SDK to use our access token for Discord API calls
2. Once the token is updated, we call [`Client::Connect`] in the callback
3. The SDK will begin connecting asynchronously
4. Our status text will tell you when the SDK is ready!
### Testing the Connection
Press play and click the button again to log in and authorize the game. This time you should see the status text change as it goes through the OAuth flow, and it should end up `Ready`
### Troubleshooting
If you don't see `Ready` status:
* Check that your access token is valid
* Ensure you have internet connectivity
* Look for error messages from the SDK in the console
* Verify your `ClientID` set in the inspector is correct
```cs theme={"system"}
using UnityEngine;
using UnityEngine.UI;
using Discord.Sdk;
using System.Linq;
public class DiscordManager : MonoBehaviour
{
[SerializeField]
private ulong clientId; // Set this in the Unity Inspector from the dev portal
[SerializeField]
private Button loginButton;
[SerializeField]
private Text statusText;
private Client client;
private string codeVerifier;
void Start()
{
client = new Client();
// Modifying LoggingSeverity will show you more or less logging information
client.AddLogCallback(OnLog, LoggingSeverity.Error);
client.SetStatusChangedCallback(OnStatusChanged);
// Make sure the button has a listener
if (loginButton != null)
{
loginButton.onClick.AddListener(StartOAuthFlow);
}
else
{
Debug.LogError("Login button reference is missing, connect it in the inspector!");
}
// Set initial status text
if (statusText != null)
{
statusText.text = "Ready to login";
}
else
{
Debug.LogError("Status text reference is missing, connect it in the inspector!");
}
}
private void OnLog(string message, LoggingSeverity severity)
{
Debug.Log($"Log: {severity} - {message}");
}
private void OnStatusChanged(Client.Status status, Client.Error error, int errorCode)
{
Debug.Log($"Status changed: {status}");
statusText.text = status.ToString();
if(error != Client.Error.None)
{
Debug.LogError($"Error: {error}, code: {errorCode}");
}
}
private void StartOAuthFlow() {
var authorizationVerifier = client.CreateAuthorizationCodeVerifier();
codeVerifier = authorizationVerifier.Verifier();
var args = new AuthorizationArgs();
args.SetClientId(clientId);
args.SetScopes(Client.GetDefaultPresenceScopes());
args.SetCodeChallenge(authorizationVerifier.Challenge());
client.Authorize(args, OnAuthorizeResult);
}
private void OnAuthorizeResult(ClientResult result, string code, string redirectUri) {
Debug.Log($"Authorization result: [{result.Error()}] [{code}] [{redirectUri}]");
if (!result.Successful()) {
return;
}
GetTokenFromCode(code, redirectUri);
}
private void GetTokenFromCode(string code, string redirectUri) {
client.GetToken(clientId,
code,
codeVerifier,
redirectUri,
(result, token, refreshToken, tokenType, expiresIn, scope) => {
if (token != "") {
OnReceivedToken(token);
} else {
OnRetrieveTokenFailed();
}
});
}
private void OnReceivedToken(string token) {
Debug.Log("Token received: " + token);
client.UpdateToken(AuthorizationTokenType.Bearer, token, (ClientResult result) => { client.Connect(); });
}
private void OnRetrieveTokenFailed() { statusText.text = "Failed to retrieve token"; }
}
```
Now that your client is in a ready state, we can start implementing Discord social features.
***
## Step 9: Access Discord Relationships
Let's access the user's Discord relationships (friends list) and display the count. This will help you understand how to access and use Discord data in your game.
Lets add some new code to your `DiscordManager.cs`:
```cs theme={"system"}
private void ClientReady()
{
Debug.Log($"Friend Count: {client.GetRelationships().Count()}");
}
```
We want to call this when the client is ready to use which we'll know in our `OnStatusChanged` method:
```cs theme={"system"}
private void OnStatusChanged(Client.Status status, Client.Error error, int errorCode)
{
Debug.Log($"Status changed: {status}");
statusText.text = status.ToString();
if(error != Client.Error.None)
{
Debug.LogError($"Error: {error}, code: {errorCode}");
}
if (status == Client.Status.Ready)
{
ClientReady();
}
}
```
### What This Code Does
When the client status is `Ready,` it'll call our `ClientReady` function, which will call [`Client::GetRelationships`], returning us a list of all the player's friends. We then log the number of friends directly to the console.
### Testing It Out
Hit play and then click the button. Once the OAuth flow completes and you see the status hit `Ready` check the console to see the output!
### Example Output
```
Friend Count: 42
```
### Troubleshooting
* Verify your OAuth2 scopes include relationships access
* Ensure you're connected (status is `Ready`)
* Check that you have friends on Discord
* Look for errors in the logging callback
Next, we'll learn how to show your game's activity on Discord with Rich Presence!
```cs theme={"system"}
using UnityEngine;
using UnityEngine.UI;
using Discord.Sdk;
using System.Linq;
public class DiscordManager : MonoBehaviour
{
[SerializeField]
private ulong clientId; // Set this in the Unity Inspector from the dev portal
[SerializeField]
private Button loginButton;
[SerializeField]
private Text statusText;
private Client client;
private string codeVerifier;
void Start()
{
client = new Client();
// Modifying LoggingSeverity will show you more or less logging information
client.AddLogCallback(OnLog, LoggingSeverity.Error);
client.SetStatusChangedCallback(OnStatusChanged);
// Make sure the button has a listener
if (loginButton != null)
{
loginButton.onClick.AddListener(StartOAuthFlow);
}
else
{
Debug.LogError("Login button reference is missing, connect it in the inspector!");
}
// Set initial status text
if (statusText != null)
{
statusText.text = "Ready to login";
}
else
{
Debug.LogError("Status text reference is missing, connect it in the inspector!");
}
}
private void OnLog(string message, LoggingSeverity severity)
{
Debug.Log($"Log: {severity} - {message}");
}
private void OnStatusChanged(Client.Status status, Client.Error error, int errorCode)
{
Debug.Log($"Status changed: {status}");
statusText.text = status.ToString();
if(error != Client.Error.None)
{
Debug.LogError($"Error: {error}, code: {errorCode}");
}
if (status == Client.Status.Ready)
{
ClientReady();
}
}
private void ClientReady()
{
Debug.Log($"Friend Count: {client.GetRelationships().Count()}");
}
private void StartOAuthFlow() {
var authorizationVerifier = client.CreateAuthorizationCodeVerifier();
codeVerifier = authorizationVerifier.Verifier();
var args = new AuthorizationArgs();
args.SetClientId(clientId);
args.SetScopes(Client.GetDefaultPresenceScopes());
args.SetCodeChallenge(authorizationVerifier.Challenge());
client.Authorize(args, OnAuthorizeResult);
}
private void OnAuthorizeResult(ClientResult result, string code, string redirectUri) {
Debug.Log($"Authorization result: [{result.Error()}] [{code}] [{redirectUri}]");
if (!result.Successful()) {
return;
}
GetTokenFromCode(code, redirectUri);
}
private void GetTokenFromCode(string code, string redirectUri) {
client.GetToken(clientId,
code,
codeVerifier,
redirectUri,
(result, token, refreshToken, tokenType, expiresIn, scope) => {
if (token != "") {
OnReceivedToken(token);
} else {
OnRetrieveTokenFailed();
}
});
}
private void OnReceivedToken(string token) {
Debug.Log("Token received: " + token);
client.UpdateToken(AuthorizationTokenType.Bearer, token, (ClientResult result) => { client.Connect(); });
}
private void OnRetrieveTokenFailed() { statusText.text = "Failed to retrieve token"; }
}
```
***
## Step 10: Set Rich Presence
Let's show your game's activity on Discord using Rich Presence. This feature lets players see what others are doing in your game directly in their Discord friends list.
Update your `ClientReady` method with this code:
```cs theme={"system"}
private void ClientReady()
{
Debug.Log($"Friend Count: {client.GetRelationships().Count()}");
Activity activity = new Activity();
activity.SetType(ActivityTypes.Playing);
activity.SetState("In Competitive Match");
activity.SetDetails("Rank: Diamond II");
client.UpdateRichPresence(activity, (ClientResult result) => {
if (result.Successful()) {
Debug.Log("Rich presence updated!");
} else {
Debug.LogError("Failed to update rich presence");
}
});
}
```
### What This Code Does
1. Creates an [`Activity`] object to represent what the player is doing
2. Sets basic information like:
* The activity type (Playing)
* Current state ("In Competitive Match")
* Additional details ("Rank: Diamond II")
3. Updates your rich presence on Discord
### Testing It Out
Hit play and then click the button. Once the OAuth flow is complete, you will see the status hit `Ready`. The console will tell you if setting rich presence was successful, and you can check your Discord profile to see it!
### Troubleshooting
If you don't see your presence:
* Ensure you're connected (status is `Ready`)
* Check the console for error messages
* Verify your activity settings are valid
* Make sure you're not invisible on Discord
***
```cs theme={"system"}
using UnityEngine;
using UnityEngine.UI;
using Discord.Sdk;
using System.Linq;
public class DiscordManager : MonoBehaviour
{
[SerializeField]
private ulong clientId; // Set this in the Unity Inspector from the dev portal
[SerializeField]
private Button loginButton;
[SerializeField]
private Text statusText;
private Client client;
private string codeVerifier;
void Start()
{
client = new Client();
// Modifying LoggingSeverity will show you more or less logging information
client.AddLogCallback(OnLog, LoggingSeverity.Error);
client.SetStatusChangedCallback(OnStatusChanged);
// Make sure the button has a listener
if (loginButton != null)
{
loginButton.onClick.AddListener(StartOAuthFlow);
}
else
{
Debug.LogError("Login button reference is missing, connect it in the inspector!");
}
// Set initial status text
if (statusText != null)
{
statusText.text = "Ready to login";
}
else
{
Debug.LogError("Status text reference is missing, connect it in the inspector!");
}
}
private void OnLog(string message, LoggingSeverity severity)
{
Debug.Log($"Log: {severity} - {message}");
}
private void OnStatusChanged(Client.Status status, Client.Error error, int errorCode)
{
Debug.Log($"Status changed: {status}");
statusText.text = status.ToString();
if(error != Client.Error.None)
{
Debug.LogError($"Error: {error}, code: {errorCode}");
}
if (status == Client.Status.Ready)
{
ClientReady();
}
}
private void ClientReady()
{
Debug.Log($"Friend Count: {client.GetRelationships().Count()}");
Activity activity = new Activity();
activity.SetType(ActivityTypes.Playing);
activity.SetState("In Competitive Match");
activity.SetDetails("Rank: Diamond II");
client.UpdateRichPresence(activity, (ClientResult result) => {
if (result.Successful()) {
Debug.Log("Rich presence updated!");
} else {
Debug.LogError("Failed to update rich presence");
}
});
}
private void StartOAuthFlow() {
var authorizationVerifier = client.CreateAuthorizationCodeVerifier();
codeVerifier = authorizationVerifier.Verifier();
var args = new AuthorizationArgs();
args.SetClientId(clientId);
args.SetScopes(Client.GetDefaultPresenceScopes());
args.SetCodeChallenge(authorizationVerifier.Challenge());
client.Authorize(args, OnAuthorizeResult);
}
private void OnAuthorizeResult(ClientResult result, string code, string redirectUri) {
Debug.Log($"Authorization result: [{result.Error()}] [{code}] [{redirectUri}]");
if (!result.Successful()) {
return;
}
GetTokenFromCode(code, redirectUri);
}
private void GetTokenFromCode(string code, string redirectUri) {
client.GetToken(clientId,
code,
codeVerifier,
redirectUri,
(result, token, refreshToken, tokenType, expiresIn, scope) => {
if (token != "") {
OnReceivedToken(token);
} else {
OnRetrieveTokenFailed();
}
});
}
private void OnReceivedToken(string token) {
Debug.Log("Token received: " + token);
client.UpdateToken(AuthorizationTokenType.Bearer, token, (ClientResult result) => { client.Connect(); });
}
private void OnRetrieveTokenFailed() { statusText.text = "Failed to retrieve token"; }
}
```
***
## Conclusion
Congratulations! You've successfully integrated the Discord Social SDK into Unity. Let's review what you've accomplished:
### What You've Built
* ✅ Created a Discord application and configured OAuth2
* ✅ Set up SDK logging and status monitoring
* ✅ Implemented user authentication flow
* ✅ Retrieved Discord relationships data
* ✅ Added Rich Presence support
### Key Concepts Learned
* How to initialize and configure the Discord SDK
* Managing authentication and connections
* Working with Discord's social features
* Handling asynchronous callbacks
* Monitoring SDK status and events
***
## Next Steps
### Social SDK Unity sample
Check out our [in depth sample for using the Social SDK in Unity](https://github.com/discord/social-sdk-unity-sample) following the best practices laid out in these guides. It contains easy to drop in prefabs with both code and UI to quickly integrate Discord's social features into your game.
}>
Create a unified friends list combining Discord and game-specific friendships
}>
Customize your game's rich presence to show more advanced information and game invites
}>
Allow players to invite friends to join their game session or party.
Need help? Join the [Discord Developers Server](https://discord.gg/discord-developers) and share questions in the `#social-sdk-dev-help` channel for support from the community.
If you encounter a bug while working with the Social SDK, please report it here: [https://dis.gd/social-sdk-bug-report](https://dis.gd/social-sdk-bug-report)
***
## Change Log
| Date | Changes |
| -------------- | --------------- |
| March 17, 2025 | Initial release |
[`Activity`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#ae793d9adbe16fef402b859ba02bee682
[`Client::Authorize`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ace94a58e27545a933d79db32b387a468
[`Client::Connect`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a873a844c7c4c72e9e693419bb3e290aa
[`Client::GetRelationships`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ad481849835cd570f0e03adafcf90125d
[`Client::UpdateToken`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a606b32cef7796f7fb91c2497bc31afc4
[`RunCallbacks`]: https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#ab5dd8cf274f581ee1885de5816be3c29
# Getting Started with Unreal Engine and the Discord Social SDK
Source: https://docs.discord.com/developers/discord-social-sdk/getting-started/using-unreal-engine
Integrate the Discord Social SDK into Unreal Engine games.
## Overview
This guide will walk you through integrating the Discord Social SDK into an Unreal Engine project. By the end, you'll have a project that can:
* Authenticate users with Discord
* Set up logging and status monitoring
* Start the SDK and establish a connection
* Request the number of Discord friends the player has
For the initial launch, the Unreal plugin has a name of `DiscordPartnerSDK` but this name will be changing to `DiscordSocialSDK` in a future version
### Prerequisites
We are going to make a simple Unreal console application for this guide. Make sure you have the following prerequisites:
* Windows
* Visual Studio 2022
* Unreal Engine 5.5
Let's walk through the steps in detail.
***
## Step 1: Create a Discord Developer Team
Before you start, you'll need to create a developer team on the Discord Developer Portal. This team will be used to manage your Discord applications and SDK integrations.
If you already have a team configured, you can skip this step.
1. Create a developer team on the [Discord Developer Portal](https://discord.com/developers/teams).
Later, you can invite your team members to your new team to collaborate on your integration.
***
## Step 2: Create a Discord Application
1. Create a new application on the [Discord Developer Portal](https://discord.com/developers/applications) and assign it to your team.
2. Add a redirect URL in the [OAuth2 tab](https://discord.com/developers/applications/select/oauth2):
* For desktop applications: `http://127.0.0.1/callback` (this can be changed later).
* See [`discordpp::Client::Authorize`](https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ace94a58e27545a933d79db32b387a468) for more details on setting up more advanced redirect URIs.
3. Enable the `Public Client` toggle in the [OAuth2 tab](https://discord.com/developers/applications/select/oauth2).
This guide requires enabling **Public Client** to allow you to get started with the SDK quickly. Most games will not want to ship as a public client. This setting should be reviewed by your team prior to releasing your game. [Learn more about public clients](/developers/discord-social-sdk/core-concepts/oauth2-scopes#oauth2-client-types).
***
## Step 3: Enable Discord Social SDK for Your App
Once you've created your Discord application, you'll need to enable the Discord Social SDK for it.
1. From the [Discord Developer Portal](https://discord.com/developers/applications), select your newly created application from Step 2.
2. In the left sidebar for your app, locate and click the [Getting Started](https://discord.com/developers/applications/select/social-sdk/getting-started) link under `Discord Social SDK`.
3. Fill out the form to share details about your game.
4. Click `Submit` and the Social SDK will be enabled for your application.
5. Once enabled, you'll find binaries for the Social SDK under [Downloads](https://discord.com/developers/applications/select/social-sdk/downloads).
***
## Step 4: Download the Social SDK for Unreal Engine
1. Click on the [Downloads](https://discord.com/developers/applications/select/social-sdk/downloads) link under the Discord Social SDK section of the sidebar.
2. Select the latest version from the version dropdown and download the SDK for Unreal Engine.
A Unreal Engine sample project is available for download on this page, but we are not going to cover it in this guide. Explore it on your own after you finish this guide!
***
## Step 5: Project Setup
### Create a new Unreal Engine project
Select Third Person Unreal project (C++ language). We'll set the project name to `DiscordSocialUnreal`.
The code samples below assume the project name is `DiscordSocialUnreal`. If you use a different name, make sure to replace it in the code.
Wait for the shaders to compile. This can take a while.
### Add the Discord SDK to the project
Locate your Unreal project in the Windows file explorer.
Create a new directory named `Plugins` in the base of your project directory (`DiscordSocialUnreal/Plugins`).
Extract the Discord SDK archive in the Plugins directory. You should end up with a directory called `DiscordPartnerSDK` inside of Plugins `DiscordSocialUnreal/Plugins/DiscordPartnerSDK`.
Double check the name of the `DiscordPartnerSDK` folder once you've extracted it. It may change between versions and you'll need to add the correct name in the code below.
Reload Unreal Editor and open the project. It should ask if you'd like to recompile the `DiscordPartnerSDK` plugin on launch.
Open Visual Studio from the `Tools -> Open Visual Studio` menu in Unreal Editor.
Open your `DiscordSocialUnreal.Build.cs` file located at `/Source/DiscordSocialUnreal/` and add the following line after `PublicDependencyModuleNames` array:
```cpp theme={"system"}
PrivateDependencyModuleNames.AddRange(new string[] { "DiscordPartnerSDK" });
```
Back in Unreal Editor, refresh the Visual Studio Project from the `Tools -> Refresh Visual Studio Project` menu.
In the Unreal Editor, compile the code with `Ctrl-Alt-F11`. You should see a "Live coding succeeded" modal window if the compilation is successful.
***
## Step 6: Initialize the SDK
In this section, we'll add callbacks for logging and status of the SDK and OAuth2 authentication to support account linking with Discord. This process will:
1. Open the Discord app or a browser window for Discord login
2. Get an authorization code
3. Exchange it for an access token
4. Connect to Discord
**Building for mobile?** On iOS and Android you only need to configure your Discord Application ID in Project Settings. Set that up in the [Account Linking on Mobile](/developers/discord-social-sdk/development-guides/account-linking-on-mobile) guide, then continue here. The rest of this step is the same for mobile.
**Choosing your OAuth2 scopes:** This guide uses `UDiscordClient::GetDefaultPresenceScopes()`, which requests the `openid` and `sdk.social_layer_presence` scopes. These enable core features like account linking, friends list, and rich presence.
If your game also needs lobbies, voice chat, or direct messaging, use `UDiscordClient::GetDefaultCommunicationScopes()` instead.
See the [OAuth2 Scopes guide](/developers/discord-social-sdk/core-concepts/oauth2-scopes) for the full breakdown.
Open `DiscordSocialUnrealCharacter.h` in Visual Studio, where we start integrating the Social SDK!
### DiscordLocalPlayerSubsystem
To use the Social SDK with Unreal Engine, we will need to include the `DiscordLocalPlayerSubsystem` which is responsible for managing the lifecycle of the client and executing the [`RunCallbacks`] event loop.
Add the following code to `DiscordSocialUnrealCharacter.h`:
```cpp theme={"system"}
#include "DiscordLocalPlayerSubsystem.h"
```
Now let's add the following functions to the public section of the DiscordSocialUnrealCharacter class:
##### **DiscordSocialUnrealCharacter.h**
```cpp theme={"system"}
UFUNCTION(Exec)
void DiscordConnect();
UFUNCTION()
void OnStatusChanged(EDiscordClientStatus Status, EDiscordClientError Error, int32 ErrorDetail);
UPROPERTY()
UDiscordAuthorizationCodeVerifier* CodeVerifier;
UPROPERTY()
UDiscordLocalPlayerSubsystem* Discord;
```
And add the following functions to the protected section of the DiscordSocialUnrealCharacter class:
##### **DiscordSocialUnrealCharacter.h**
```cpp theme={"system"}
void BeginPlay();
void OnLogMessage(FString Message, EDiscordLoggingSeverity Severity);
void OnAuthorizeCompleted(UDiscordClientResult* Result, FString Code, FString RedirectUri);
void OnTokenExchange(UDiscordClientResult* Result, FString AccessToken, FString RefreshToken, EDiscordAuthorizationTokenType TokenType, int32 ExpiresIn, FString Scope);
void OnTokenUpdated(UDiscordClientResult* Result);
```
Now let's add the implementation of these functions to the `DiscordSocialUnrealCharacter.cpp` file.
In the `DiscordSocialUnrealCharacter.cpp` file, add the following code:
##### **DiscordSocialUnrealCharacter.cpp**
```cpp theme={"system"}
#define APPLICATION_ID 1111111111111111111
void ADiscordSocialUnrealCharacter::BeginPlay() {
auto PlayerController = Cast(GetController());
auto LocalPlayer = PlayerController->GetLocalPlayer();
Discord = ULocalPlayer::GetSubsystem(LocalPlayer);
auto LogCallback = FDiscordClientLogCallback::CreateUObject(this, &ADiscordSocialUnrealCharacter::OnLogMessage);
FScriptDelegate StatusChanged;
StatusChanged.BindUFunction(this, "OnStatusChanged");
Discord->Client->AddLogCallback(LogCallback, EDiscordLoggingSeverity::Info);
Discord->OnStatusChanged.Add(StatusChanged);
}
void ADiscordSocialUnrealCharacter::DiscordConnect() {
CodeVerifier = Discord->Client->CreateAuthorizationCodeVerifier();
auto AuthArgs = NewObject();
AuthArgs->Init();
AuthArgs->SetClientId(APPLICATION_ID);
AuthArgs->SetScopes(UDiscordClient::GetDefaultPresenceScopes());
AuthArgs->SetCodeChallenge(CodeVerifier->Challenge());
Discord->Client->Authorize(AuthArgs, FDiscordClientAuthorizationCallback::CreateUObject(this, &ADiscordSocialUnrealCharacter::OnAuthorizeCompleted));
}
void ADiscordSocialUnrealCharacter::OnLogMessage(FString Message, EDiscordLoggingSeverity Severity) {
UE_LOG(LogTemplateCharacter, Log, TEXT("[%s] %s"), *UEnum::GetValueAsString(Severity), *Message);
}
void ADiscordSocialUnrealCharacter::OnStatusChanged(EDiscordClientStatus Status, EDiscordClientError Error, int32 ErrorDetail) {
UE_LOG(LogTemplateCharacter, Log, TEXT("Connection status: %s"), *UEnum::GetValueAsString(Status));
if (Status == EDiscordClientStatus::Ready) {
UE_LOG(LogTemplateCharacter, Log, TEXT("Connected to Discord! Ready to go! You can now start using Discord features."));
}
else if (Error != EDiscordClientError::None) {
UE_LOG(LogTemplateCharacter, Log, TEXT("Connection error: %s (Detail: %d)"), *UEnum::GetValueAsString(Error), ErrorDetail);
}
}
void ADiscordSocialUnrealCharacter::OnAuthorizeCompleted(UDiscordClientResult* Result, FString Code, FString RedirectUri) {
if (!Result->Successful()) {
UE_LOG( LogTemplateCharacter, Error, TEXT("Discord authorization failed: %s"), *Result->Error());
return;
}
UE_LOG(LogTemplateCharacter, Log, TEXT("Authorization successful! Getting access token..."));
Discord->Client->GetToken(APPLICATION_ID, Code, CodeVerifier->Verifier(), RedirectUri, FDiscordClientTokenExchangeCallback::CreateUObject(this, &ADiscordSocialUnrealCharacter::OnTokenExchange));
}
void ADiscordSocialUnrealCharacter::OnTokenExchange(UDiscordClientResult* Result, FString AccessToken, FString RefreshToken, EDiscordAuthorizationTokenType TokenType, int32 ExpiresIn, FString Scope) {
if (!Result->Successful()) {
UE_LOG(LogTemplateCharacter, Error, TEXT("Discord token exchange failed: %s"), *Result->Error());
}
Discord->Client->UpdateToken(TokenType, AccessToken, FDiscordClientUpdateTokenCallback::CreateUObject(this, &ADiscordSocialUnrealCharacter::OnTokenUpdated));
}
void ADiscordSocialUnrealCharacter::OnTokenUpdated(UDiscordClientResult* Result) {
Discord->Client->Connect();
}
```
### What's Happening Here?
1. We create a code verifier for OAuth2 PKCE security
2. Set up authorization arguments with your app ID and required scopes
3. Start the auth flow with [`Client::Authorize`], which opens a browser
4. When authorized, we exchange the code for an access token
5. [`Client::UpdateToken`] tells the SDK to use our access token for Discord API calls
6. Once the token is updated, we call [`Client::Connect`], and the SDK will begin connecting asynchronously
### Testing It Out
Save your changes in Visual Studio and return to Unreal Editor.
Compile the code in Unreal Editor with `Ctrl-Alt-F11`
Enter play mode and find the console at the bottom of Unreal Editor. Run `DiscordConnect` as a command to start the account linking process.
```
DiscordConnect
```
If Discord is open on your computer, you should see a prompt to authorize the connection. Click "Authorize" to continue.
If the connection is successful, you should see a message in the Unreal Editor console that says, "Connected to Discord! Ready to go! You can now start using Discord features."
Most Discord features won't work until the status is `Ready`. The status callback lets you know when you can start using them.
### Troubleshooting
* Double check your `APPLICATION_ID` is correct
* Ensure you've added the redirect URL in your Discord Developer Portal
* Check the output log for specific error messages
* Check that your access token is valid
* Ensure you have internet connectivity
On Mac you may get the error "libdiscord\_partner\_sdk.dylib" Not Opened because Apple couldn't verify it. If this happens press **Done** on the popup.
You'll need to open your **System Settings > Privacy & Security** and scroll down to the **Security** section. It will tell you "libdiscord\_partner\_sdk.dylib" was blocked to protect your Mac. Press **Open Anyway** and try running again.
Now when you get the pop up you'll have the option to select **Open Anyway** and it will be able to use it successfully.
Now that your client is in a ready state, we can start implementing Discord social features.
## Step 7: Access Discord Relationships
Let's access the user's Discord relationships (friends list) and display the count. This will help you understand how to access and use Discord data in your game.
Add this code to the `Ready` section of `OnStatusChanged`:
```cpp theme={"system"}
if (Status == EDiscordClientStatus::Ready) {
UE_LOG(LogTemplateCharacter, Log, TEXT("Connected to Discord! Ready to go! You can now start using Discord features."));
UE_LOG(LogTemplateCharacter, Log, TEXT("👥 Friends Count: %d"), Discord->Client->GetRelationships().Num());
}
```
### What This Code Does
Now, when the client status is `Ready,` we'll call [`Client::GetRelationships`], returning us a list of all the player's friends. We then log the number of friends directly to the output log.
### Testing It Out
Save your changes in Visual Studio and return to Unreal Editor.
Compile the code in Unreal Editor with `Ctrl-Alt-F11`
Enter play mode, and find the console at the bottom of Unreal Editor, run `DiscordConnect`.
### Example Output
```
👥 Friends Count: 42
```
### Troubleshooting
* Verify your OAuth2 scopes include relationships access
* Ensure you're connected (status is `Ready`)
* Check that you have friends on Discord
* Look for errors in the output log
Next, we'll learn how to show your game's activity on Discord with Rich Presence!
## Step 8: Set Rich Presence
Let's show your game's activity on Discord using Rich Presence. This feature lets players see what others are doing in your game directly in their Discord friends list.
Add this code to the `Ready` section of `OnStatusChanged`:
```cpp theme={"system"}
if (Status == EDiscordClientStatus::Ready) {
UE_LOG(LogTemplateCharacter, Log, TEXT("Connected to Discord! Ready to go! You can now start using Discord features."));
UE_LOG(LogTemplateCharacter, Log, TEXT("👥 Friends Count: %d"), Discord->Client->GetRelationships().Num());
UE_LOG(LogTemplateCharacter, Log, TEXT("Connected to Discord! Ready to go! You can now start using Discord features."));
UDiscordActivity* Activity = NewObject();
Activity->Init();
Activity->SetType(EDiscordActivityTypes::Playing);
Activity->SetState("In competitive match");
Activity->SetDetails("Rank: Diamond II");
Discord->Client->UpdateRichPresence(Activity, FDiscordClientUpdateRichPresenceCallback::CreateUObject(this, &ADiscordSocialUnrealCharacter::OnRichPresenceUpdated));
}
```
Then add this new method:
```cpp theme={"system"}
void ADiscordSocialUnrealCharacter::OnRichPresenceUpdated(UDiscordClientResult* Result) {
if (Result->Successful()) {
UE_LOG(LogTemplateCharacter, Log, TEXT("Rich Presence updated successfully!"));
} else {
UE_LOG(LogTemplateCharacter, Error, TEXT("Rich Presence update failed"));
}
}
```
### What This Code Does
1. Creates an [`Activity`] object to represent what the player is doing
2. Sets basic information like:
* The activity type (Playing)
* Current state ("In Competitive Match")
* Additional details ("Rank: Diamond II")
3. Updates your rich presence on Discord
### Testing It Out
Save your changes in Visual Studio and return to Unreal Editor.
Compile the code in Unreal Editor with `Ctrl-Alt-F11`
Enter play mode and find the console at the bottom of Unreal Editor. Run `DiscordConnect.`
Once the OAuth flow is complete, the output log will tell you if setting rich presence was successful, and you can check your Discord profile to see it!
### Troubleshooting
If you don't see your presence:
* Ensure you're connected (status is `Ready`)
* Check the output log for error messages
* Verify your activity settings are valid
* Make sure you're not invisible on Discord
***
```cs theme={"system"}
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
public class DiscordSocialUnreal : ModuleRules
{
public DiscordSocialUnreal(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput" });
PrivateDependencyModuleNames.AddRange(new string[] { "DiscordPartnerSDK" });
}
}
```
```cpp theme={"system"}
// Copyright Epic Games, Inc. All Rights Reserved.
#include "DiscordSocialUnrealCharacter.h"
#include "Engine/LocalPlayer.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "GameFramework/Controller.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "InputActionValue.h"
DEFINE_LOG_CATEGORY(LogTemplateCharacter);
#define APPLICATION_ID 1111111111111111111
void ADiscordSocialUnrealCharacter::BeginPlay() {
auto PlayerController = Cast(GetController());
auto LocalPlayer = PlayerController->GetLocalPlayer();
Discord = ULocalPlayer::GetSubsystem(LocalPlayer);
auto LogCallback = FDiscordClientLogCallback::CreateUObject(this, &ADiscordSocialUnrealCharacter::OnLogMessage);
FScriptDelegate StatusChanged;
StatusChanged.BindUFunction(this, "OnStatusChanged");
Discord->Client->AddLogCallback(LogCallback, EDiscordLoggingSeverity::Info);
Discord->OnStatusChanged.Add(StatusChanged);
}
void ADiscordSocialUnrealCharacter::DiscordConnect() {
CodeVerifier = Discord->Client->CreateAuthorizationCodeVerifier();
auto AuthArgs = NewObject();
AuthArgs->Init();
AuthArgs->SetClientId(APPLICATION_ID);
AuthArgs->SetScopes(UDiscordClient::GetDefaultPresenceScopes());
AuthArgs->SetCodeChallenge(CodeVerifier->Challenge());
Discord->Client->Authorize(AuthArgs, FDiscordClientAuthorizationCallback::CreateUObject(this, &ADiscordSocialUnrealCharacter::OnAuthorizeCompleted));
}
void ADiscordSocialUnrealCharacter::OnLogMessage(FString Message, EDiscordLoggingSeverity Severity) {
UE_LOG(LogTemplateCharacter, Log, TEXT("[%s] %s"), *UEnum::GetValueAsString(Severity), *Message);
}
void ADiscordSocialUnrealCharacter::OnStatusChanged(EDiscordClientStatus Status, EDiscordClientError Error, int32 ErrorDetail) {
UE_LOG(LogTemplateCharacter, Log, TEXT("Connection status: %s"), *UEnum::GetValueAsString(Status));
if (Status == EDiscordClientStatus::Ready) {
UE_LOG(LogTemplateCharacter, Log, TEXT("Connected to Discord! Ready to go! You can now start using Discord features."));
UE_LOG(LogTemplateCharacter, Log, TEXT("Number of Relationships: %d"), Discord->Client->GetRelationships().Num());
UDiscordActivity activity;
activity.SetDetails("In competitive");
activity.SetState("Diamond II");
}
else if (Error != EDiscordClientError::None) {
UE_LOG(LogTemplateCharacter, Log, TEXT("Connection error: %s (Detail: %d)"), *UEnum::GetValueAsString(Error), ErrorDetail);
}
}
void ADiscordSocialUnrealCharacter::OnAuthorizeCompleted(UDiscordClientResult* Result, FString Code, FString RedirectUri) {
if (!Result->Successful()) {
UE_LOG(LogTemplateCharacter, Error, TEXT("Discord authorization failed: %s"), *Result->Error());
return;
}
UE_LOG(LogTemplateCharacter, Log, TEXT("Authorization successful! Getting access token..."));
Discord->Client->GetToken(APPLICATION_ID, Code, CodeVerifier->Verifier(), RedirectUri, FDiscordClientTokenExchangeCallback::CreateUObject(this, &ADiscordSocialUnrealCharacter::OnTokenExchange));
}
void ADiscordSocialUnrealCharacter::OnTokenExchange(UDiscordClientResult* Result, FString AccessToken, FString RefreshToken, EDiscordAuthorizationTokenType TokenType, int32 ExpiresIn, FString Scope) {
if (!Result->Successful()) {
UE_LOG(LogTemplateCharacter, Error, TEXT("Discord token exchange failed: %s"), *Result->Error());
}
Discord->Client->UpdateToken(TokenType, AccessToken, FDiscordClientUpdateTokenCallback::CreateUObject(this, &ADiscordSocialUnrealCharacter::OnTokenUpdated));
}
void ADiscordSocialUnrealCharacter::OnTokenUpdated(UDiscordClientResult* Result) {
Discord->Client->Connect();
}
//////////////////////////////////////////////////////////////////////////
// ADiscordSocialUnrealCharacter
ADiscordSocialUnrealCharacter::ADiscordSocialUnrealCharacter()
{
// Set size for collision capsule
GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);
// Don't rotate when the controller rotates. Let that just affect the camera.
bUseControllerRotationPitch = false;
bUseControllerRotationYaw = false;
bUseControllerRotationRoll = false;
// Configure character movement
GetCharacterMovement()->bOrientRotationToMovement = true; // Character moves in the direction of input...
GetCharacterMovement()->RotationRate = FRotator(0.0f, 500.0f, 0.0f); // ...at this rotation rate
// Note: For faster iteration times these variables, and many more, can be tweaked in the Character Blueprint
// instead of recompiling to adjust them
GetCharacterMovement()->JumpZVelocity = 700.f;
GetCharacterMovement()->AirControl = 0.35f;
GetCharacterMovement()->MaxWalkSpeed = 500.f;
GetCharacterMovement()->MinAnalogWalkSpeed = 20.f;
GetCharacterMovement()->BrakingDecelerationWalking = 2000.f;
GetCharacterMovement()->BrakingDecelerationFalling = 1500.0f;
// Create a camera boom (pulls in towards the player if there is a collision)
CameraBoom = CreateDefaultSubobject(TEXT("CameraBoom"));
CameraBoom->SetupAttachment(RootComponent);
CameraBoom->TargetArmLength = 400.0f; // The camera follows at this distance behind the character
CameraBoom->bUsePawnControlRotation = true; // Rotate the arm based on the controller
// Create a follow camera
FollowCamera = CreateDefaultSubobject(TEXT("FollowCamera"));
FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName); // Attach the camera to the end of the boom and let the boom adjust to match the controller orientation
FollowCamera->bUsePawnControlRotation = false; // Camera does not rotate relative to arm
// Note: The skeletal mesh and anim blueprint references on the Mesh component (inherited from Character)
// are set in the derived blueprint asset named ThirdPersonCharacter (to avoid direct content references in C++)
}
//////////////////////////////////////////////////////////////////////////
// Input
void ADiscordSocialUnrealCharacter::NotifyControllerChanged()
{
Super::NotifyControllerChanged();
// Add Input Mapping Context
if (APlayerController* PlayerController = Cast(Controller))
{
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem(PlayerController->GetLocalPlayer()))
{
Subsystem->AddMappingContext(DefaultMappingContext, 0);
}
}
}
void ADiscordSocialUnrealCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
// Set up action bindings
if (UEnhancedInputComponent* EnhancedInputComponent = Cast(PlayerInputComponent)) {
// Jumping
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Started, this, &ACharacter::Jump);
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &ACharacter::StopJumping);
// Moving
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &ADiscordSocialUnrealCharacter::Move);
// Looking
EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &ADiscordSocialUnrealCharacter::Look);
}
else
{
UE_LOG(LogTemplateCharacter, Error, TEXT("'%s' Failed to find an Enhanced Input component! This template is built to use the Enhanced Input system. If you intend to use the legacy system, then you will need to update this C++ file."), *GetNameSafe(this));
}
}
void ADiscordSocialUnrealCharacter::Move(const FInputActionValue& Value)
{
// input is a Vector2D
FVector2D MovementVector = Value.Get();
if (Controller != nullptr)
{
// find out which way is forward
const FRotator Rotation = Controller->GetControlRotation();
const FRotator YawRotation(0, Rotation.Yaw, 0);
// get forward vector
const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
// get right vector
const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
// add movement
AddMovementInput(ForwardDirection, MovementVector.Y);
AddMovementInput(RightDirection, MovementVector.X);
}
}
void ADiscordSocialUnrealCharacter::Look(const FInputActionValue& Value)
{
// input is a Vector2D
FVector2D LookAxisVector = Value.Get();
if (Controller != nullptr)
{
// add yaw and pitch input to controller
AddControllerYawInput(LookAxisVector.X);
AddControllerPitchInput(LookAxisVector.Y);
}
}
```
```cpp theme={"system"}
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "DiscordLocalPlayerSubsystem.h"
#include "GameFramework/Character.h"
#include "Logging/LogMacros.h"
#include "DiscordSocialUnrealCharacter.generated.h"
class USpringArmComponent;
class UCameraComponent;
class UInputMappingContext;
class UInputAction;
struct FInputActionValue;
DECLARE_LOG_CATEGORY_EXTERN(LogTemplateCharacter, Log, All);
UCLASS(config=Game)
class ADiscordSocialUnrealCharacter : public ACharacter
{
GENERATED_BODY()
/** Camera boom positioning the camera behind the character */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
USpringArmComponent* CameraBoom;
/** Follow camera */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
UCameraComponent* FollowCamera;
/** MappingContext */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
UInputMappingContext* DefaultMappingContext;
/** Jump Input Action */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
UInputAction* JumpAction;
/** Move Input Action */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
UInputAction* MoveAction;
/** Look Input Action */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
UInputAction* LookAction;
public:
ADiscordSocialUnrealCharacter();
UFUNCTION(Exec)
void DiscordConnect();
UFUNCTION()
void OnStatusChanged(EDiscordClientStatus Status, EDiscordClientError Error, int32 ErrorDetail);
UPROPERTY()
UDiscordAuthorizationCodeVerifier* CodeVerifier;
UPROPERTY()
UDiscordLocalPlayerSubsystem* Discord;
protected:
/** Called for movement input */
void Move(const FInputActionValue& Value);
/** Called for looking input */
void Look(const FInputActionValue& Value);
void BeginPlay();
void OnLogMessage(FString Message, EDiscordLoggingSeverity Severity);
void OnAuthorizeCompleted(UDiscordClientResult* Result, FString Code, FString RedirectUri);
void OnRichPresenceUpdated(UDiscordClientResult* Result);
void OnTokenExchange(UDiscordClientResult* Result, FString AccessToken, FString RefreshToken, EDiscordAuthorizationTokenType TokenType, int32 ExpiresIn, FString Scope);
void OnTokenUpdated(UDiscordClientResult* Result);
protected:
virtual void NotifyControllerChanged() override;
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
public:
/** Returns CameraBoom subobject **/
FORCEINLINE class USpringArmComponent* GetCameraBoom() const { return CameraBoom; }
/** Returns FollowCamera subobject **/
FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera; }
};
```
***
## Conclusion
Congratulations! You've successfully integrated the Discord Social SDK into your C++ application. Let's review what you've accomplished:
### What You've Built
* ✅ Created a Discord application and configured OAuth2
* ✅ Set up SDK logging and status monitoring
* ✅ Implemented user authentication flow
* ✅ Retrieved Discord relationships data
* ✅ Added Rich Presence support
### Key Concepts Learned
* How to initialize and configure the Discord SDK
* Managing authentication and connections
* Working with Discord's social features
* Handling asynchronous callbacks
* Monitoring SDK status and events
***
## Next Steps
You have successfully set up the Discord Social SDK with Unreal Engine and authenticated with Discord! You can now use the SDK to add more social features in your project.
}>
Create a unified friends list combining Discord and game-specific friendships
}>
Customize your game's rich presence to show more advanced information and game invites
}>
Allow players to invite friends to join their game session or party.
Need help? Join the [Discord Developers Server](https://discord.gg/discord-developers) and share questions in the `#social-sdk-dev-help` channel for support from the community.
If you encounter a bug while working with the Social SDK, please report it here: [https://dis.gd/social-sdk-bug-report](https://dis.gd/social-sdk-bug-report)
***
## Change Log
| Date | Changes |
| -------------- | --------------- |
| March 17, 2025 | initial release |
[`Activity`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#ae793d9adbe16fef402b859ba02bee682
[`Client::Authorize`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ace94a58e27545a933d79db32b387a468
[`Client::Connect`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a873a844c7c4c72e9e693419bb3e290aa
[`Client::GetRelationships`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ad481849835cd570f0e03adafcf90125d
[`Client::UpdateToken`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a606b32cef7796f7fb91c2497bc31afc4
[`RunCallbacks`]: https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#ab5dd8cf274f581ee1885de5816be3c29
# How To Guides
Source: https://docs.discord.com/developers/discord-social-sdk/how-to
How-to guides for integrating Discord Social SDK features into your game.
These how-to guides offer common solutions for integrating Discord Social SDK features into your game.
}>
Use logging and debugging tools to troubleshoot issues.
}>
Make requests to Discord's HTTP APIs from your game.
}>
Integrating and managing content moderation for your game when using the Discord Social SDK.
}>
Guidelines and best practices for announcing your Discord Social SDK integration to players.
}>
Handling Unicode characters in Discord Display Names for your game's chat and friend lists.
}>
Apply voice muting in lobbies based on Discord block relationships or external platform signals.
}>
Detect and handle rate limit errors using ClientResult.
***
## Change Log
| Date | Changes |
| ------------- | --------------------- |
| July 23, 2025 | migrated several docs |
| June 17, 2025 | added How To category |
# Debug & Log
Source: https://docs.discord.com/developers/discord-social-sdk/how-to/debug-log
Learn how to enable and use debug logging in the Discord Social SDK.
## Overview
This guide will help you understand how to install debugging symbols and handle logging to assist in building your Discord Social SDK integration.
## Debugging
Debugging symbols are hosted at [https://storage.googleapis.com/discord-public-symbols](https://storage.googleapis.com/discord-public-symbols). If using Visual Studio, you can add this link to the pdb locations under `Tools > Options > Debugging > Symbols`.
Note: You won't be able to browse files using that link, but that's ok. Individual files are accessible under the domain, and the URL functions properly as a symbol server, so it works in Visual Studio.
## Logging
You can access Discord's logs with [`Client::AddLogCallback`]. The [`Client::SetLogDir`] function will write the SDK's logs to that directory if set.
For production builds, use `ERROR (4)` or `WARN (3)` severity levels. For development builds, you can use `INFO (2)` or `VERBOSE (1)` severity levels to get more detailed logs.
```cpp theme={"system"}
client->AddLogCallback([](auto message, auto severity) {
std::cout << "[" << EnumToString(severity) << "] " << message << std::endl;
}, discordpp::LoggingSeverity::Info);
```
### Voice Logging
The voice subsystem (including the underlying WebRTC infrastructure) has its own dedicated logging separate from the main SDK log stream. You have two options:
* [`Client::SetVoiceLogDir`] — writes voice logs to disk in the specified directory.
* [`Client::AddVoiceLogCallback`] — delivers voice log messages to a callback in your application.
These can be used together, but [`Client::SetVoiceLogDir`] **must** be called immediately after Client construction,
before any call that initializes the voice subsystem (including [`Client::AddVoiceLogCallback`]). Calling it after the voice subsystem has started has no effect.
```cpp theme={"system"}
// Must be called right after constructing the client, before anything else
client->SetVoiceLogDir("/path/to/log/dir", discordpp::LoggingSeverity::Info);
// Can be called any time after construction
client->AddVoiceLogCallback([](auto message, auto severity) {
std::cout << "[Voice][" << EnumToString(severity) << "] " << message << std::endl;
}, discordpp::LoggingSeverity::Info);
```
Voice logs are generated by the voice subsystem and WebRTC layer independently of the main SDK logs captured by [`Client::AddLogCallback`]. If you are diagnosing voice or call quality issues, enable voice logging in addition to the main log callback.
### Audio Logging
For diagnosing issues with acoustic echo cancellation (AEC), you can use the [`Client::SetAecDump`] function. This
enables diagnostic recording of audio input and output waveform data, which can be invaluable when troubleshooting echo,
feedback, or other audio processing problems.
[`Client::SetAecDump`] Enables or disables AEC diagnostic recording. When enabled, the input and output waveform data will be written to the
log directory (the same directory specified by [`Client::SetLogDir`]).
```cpp theme={"system"}
// Enable AEC diagnostic recording
client->SetAecDump(true);
// ... perform voice chat operations to reproduce the issue ...
// Disable AEC diagnostic recording when done
client->SetAecDump(false);
```
**When to use:**
* Users report echo or feedback during voice chat
* Audio quality issues that may be related to echo cancellation
* Testing AEC performance in different environments
* Debugging audio processing pipeline issues
AEC dump files can become large quickly as they contain raw waveform data. Remember to disable the diagnostic recording
once you've captured the necessary data for analysis.
## Client Tools
The Discord client now includes some built-in developer tools to help you diagnose issues and test some of the features of the Discord Social SDK. To enable these new tools, navigate to `Settings > Advanced` and toggle on Application Test Mode. For the purposes of this guide, enter your Application ID and ignore the rest of the options in this modal, then click Activate. Once you've enabled Application Test Mode, you should see a new wrench icon in the upper right corner of your client. Click on this to open the developer tools.
### Account Linking
This tab shows you the status of each of the on-platform account linking flows as well as provides a quick option to start the account linking flow without needing to find one of the entry points within the client. For more information on each of the flows, read the [Account Linking from Discord guide](/developers/discord-social-sdk/development-guides/account-linking-from-discord).
***
## Next Steps
Learn how to get started with the Discord Social SDK.
Understand the core concepts of the Discord Social SDK.
Learn how to link user accounts with Discord.
Need help? Join the [Discord Developers Server](https://discord.gg/discord-developers) and share questions in the `#social-sdk-dev-help` channel for support from the community.
If you encounter a bug while working with the Social SDK, please report it here: [https://dis.gd/social-sdk-bug-report](https://dis.gd/social-sdk-bug-report)
***
## Change Log
| Date | Changes |
| ----------------- | ------------------------- |
| March 17, 2025 | initial release |
| November 17, 2025 | add client tools |
| May 4, 2026 | add voice logging section |
[`Client::AddLogCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#af78996cff24a40f5dc7066beed16692c
[`Client::AddVoiceLogCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ac4f9b459f853ef7270d1bfc12509ede5
[`Client::SetAecDump`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a3a05b2cafaa546d915a5249c63f4059f
[`Client::SetLogDir`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a23bd5802dfa3072201ea864ee839c001
[`Client::SetVoiceLogDir`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a48c6b7e8bbc2b632a935acafc6a5f7a7
# Handle Rate Limits
Source: https://docs.discord.com/developers/discord-social-sdk/how-to/handle-rate-limits
Detect and handle rate limit errors in the Discord Social SDK using ClientResult.
## Overview
This guide explains how to detect and handle rate limit errors when making requests with the Discord Social SDK.
When an SDK operation is rate limited, the callback receives a failed [`ClientResult`] where [`ClientResult::Retryable`] is `true` and [`ClientResult::RetryAfter`] contains the number of seconds to wait before retrying.
***
## Checking the Result
Every SDK operation that makes a network request passes a [`ClientResult`] to its callback. Always check [`ClientResult::Successful`] first. If the operation failed, inspect [`ClientResult::Retryable`] to decide whether to retry:
```cpp theme={"system"}
client->SendLobbyMessage(lobbyId, message, [](discordpp::ClientResult result, uint64_t messageId) {
if (result.Successful()) {
return;
}
if (result.Retryable()) {
float delaySeconds = result.RetryAfter();
// Schedule a retry after delaySeconds — see below
} else {
std::cerr << "Unrecoverable error: " << result.ToString() << "\n";
}
});
```
[`ClientResult::Retryable`] is set for rate limit responses (HTTP 429) as well as transient network errors. It is `false` for permission errors, validation failures, and other non-recoverable failures — don't retry those.
***
## Implementing Retry Logic
Use [`ClientResult::RetryAfter`] as the minimum delay before your next attempt. Retrying before this elapses will trigger continued rate limiting.
Always respect the `RetryAfter` period. Retrying before it elapses will continue to fail.
A simple retry helper using your game engine's timer system:
```cpp theme={"system"}
void SendMessageWithRetry(
std::shared_ptr client,
uint64_t lobbyId,
std::string message,
int attemptsLeft)
{
client->SendLobbyMessage(lobbyId, message,
[client, lobbyId, message, attemptsLeft](discordpp::ClientResult result, uint64_t messageId) {
if (result.Successful()) {
std::cout << "Message sent\n";
return;
}
if (result.Retryable() && attemptsLeft > 1) {
float delay = result.RetryAfter();
ScheduleCallback(delay, [client, lobbyId, message, attemptsLeft]() {
SendMessageWithRetry(client, lobbyId, message, attemptsLeft - 1);
});
} else {
std::cerr << "Failed after retries: " << result.ToString() << "\n";
}
});
}
```
`ScheduleCallback` here represents whatever timer mechanism your engine provides (e.g. a delayed task queue, coroutine sleep, or engine tick callback).
***
## Rate Limit Reference
For more information on rate limits and how to apply for increased limits for production releases, see [Communication Features](/developers/discord-social-sdk/core-concepts/communication-features). For general information on how Discord rate limiting works, see [Discord's rate limiting documentation](/developers/topics/rate-limits).
***
## Next Steps
}>
Create and manage game lobbies with text and voice chat.
}>
Send direct messages to Discord users from your game.
}>
Use logging and debugging tools to troubleshoot issues.
Need help? Join the [Discord Developers Server](https://discord.gg/discord-developers) and share questions in the `#social-sdk-dev-help` channel for support from the community.
If you encounter a bug while working with the Social SDK, please report it here: [https://dis.gd/social-sdk-bug-report](https://dis.gd/social-sdk-bug-report)
***
## Change Log
| Date | Changes |
| -------------- | --------------- |
| April 27, 2026 | Initial release |
[`ClientResult`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientResult.html#a685015ca8d29a50d47fd1ed5d469ac2e
[`ClientResult::Retryable`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientResult.html#a0d220638f4a36c0b8b731f601e0ac02d
[`ClientResult::RetryAfter`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientResult.html#ac739adca52b90d6b4cbed5753c30fd65
[`ClientResult::Successful`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientResult.html#aef3b1aca3cd156daf488ca13ae87313b
# How To Handle Special Characters in Display Names
Source: https://docs.discord.com/developers/discord-social-sdk/how-to/handle-special-characters-display-names
Properly handle and display Discord usernames with special characters and emojis.
The Social SDK [recommends](https://discord.com/developers/docs/discord-social-sdk/development-guides/creating-a-unified-friends-list#step-1-fetch-relationships) using an account's [Display Name](https://discord.com/developers/docs/slayer-sdk/classdiscordpp_1_1UserHandle.html#af6447fa2011bfa4fcd7e55bc56847f5c) in the Unified Friends List. However, Display Names support a large set of characters across the Unicode spec, and some of these characters may not be supported in a game's specified font.
There are several options to address this:
## Use a font family that supports Unicode
A family such as [Noto](https://notofonts.github.io/) aims to have broad Unicode support. If a font cannot render a Display Name, consider rendering it with a fallback font, or in the extreme case, choose a font with broad Unicode support for the Friends List.
**Tradeoffs:**
* One font family may not include 100% overlap with what Discord supports.
* A font that supports Unicode may not mesh with your game's aesthetics.
* Engine support is limited, especially for characters that rely on UTF surrogate pairs.
## Map the Unicode characters to ASCII
A library such as [Unidecode](https://metacpan.org/pod/Text::Unidecode) (which has been ported to many different languages) can map many Unicode characters to an ASCII equivalent. If you detect that a Display Name has characters that cannot be rendered, consider passing it through a library to transliterate the Display Name to ASCII.
**Tradeoffs:**
* This may result in unintended transliterations. It is impossible to create a perfect mapping between Unicode and ASCII in every situation. Libraries try their best, but this approach may unintentionally create an inaccurate or even offensive display name in some instances.
* Libraries will not have 100% coverage. Some Unicode characters may not have a map to an ASCII character. If someone's Display Name is `ʕ•͡-•ʔ` what would it map to?
* Similarly, most libraries assume English is the target language.
* Users may not appreciate their display names being changed without their knowledge or consent.
## Use the Discord Username as a Fallback
If a Display Name is not renderable at all, consider falling back to the user's [`Username`](https://discord.com/developers/docs/slayer-sdk/classdiscordpp_1_1UserHandle.html#a0eda41fe18b50bce373fb5a1b88cb411).
**Tradeoffs:**
* This can break immersion in a game if a friend had expected to rely on their Display Name from within the game.
* Some users consider their Username to be more private information and do not expect it to be shared by others (such as when someone is streaming and their friends list is visible).
***
## Change Log
| Date | Changes |
| ------------- | ------------------------------- |
| June 17, 2025 | special characters how-to added |
# Integrate Moderation
Source: https://docs.discord.com/developers/discord-social-sdk/how-to/integrate-moderation
Implement moderation features and safety controls in your application.
This guide will walk you through integrating and managing content moderation for your game when using the Discord Social SDK.
## Overview
Effective moderation is essential for creating healthy social experiences. This guide will help you:
* Better understand your moderation responsibilities
* Implement server-side moderation for text content using Discord's moderation metadata API
* Implement client-side moderation for audio content with the Discord Social SDK
## Prerequisites
Before you begin, make sure you have:
* A basic understanding of how the SDK works from the [Getting Started Guide](/developers/discord-social-sdk/getting-started)
* A basic understanding of your game's communication features
* Familiarity with [provisional accounts](/developers/discord-social-sdk/development-guides/using-provisional-accounts)
* Reviewed the [Discord Social SDK Terms](https://support-dev.discord.com/hc/en-us/articles/30225844245271-Discord-Social-SDK-Terms)
## Your Moderation Responsibilities
### Moderation on Discord
[Discord's Community Guidelines](https://discord.com/guidelines) and [Terms of Service](https://discord.com/terms) apply to any content that is rendered on Discord, including:
* Text messages, audio, and video sent within Discord's platform
* Text messages that are appear on Discord (such as in DMs or Linked Channels) after being sent by players in your game (whether they have linked their Discord account or are using a provisional account)
Discord can take various actions against such content on Discord for violating its terms or policies, including through Discord platform-wide account bans and restrictions. Actions against a player's Discord account will not affect their separate account in the game (see [below](#handling-users-with-banned-discord-accounts) for more details); however, if a player's Discord account is banned, they will no longer have access to the SDK features that require an account connection.
### Game Developer's Responsibility
Your terms and policies apply to the content in your game. You are responsible for:
* Ensuring you comply with the [Discord Social SDK Terms](https://support-dev.discord.com/hc/en-us/articles/30225844245271-Discord-Social-SDK-Terms)
* Creating game-specific content policies and enforcing them
* In-game content moderation for messages or audio within your game
* Implementing appropriate UIs for reporting and moderation; this includes providing players a way to report issues or violations of your policies and reviewing and taking appropriate action on such reports
As a reminder, you are responsible for any third-party moderation toolkits or services you use for your game and will ensure you comply with any applicable terms and laws, including obtaining consents from players as necessary for processing their data using such moderation services.
## Server-Side Chat Moderation
Discord's moderation metadata API lets your backend evaluate messages and attach application scoped
`moderation_metadata` to them. The metadata is persisted by Discord and delivered to active game
sessions via realtime `GAME_DIRECT_MESSAGE_UPDATE` or `LOBBY_MESSAGE_UPDATE` [Webhook Events](/developers/events/webhook-events) — no polling required.
Metadata is never exposed to other applications and is not included in other API endpoints or Gateway events.
### How It Works
1. Your backend receives a webhook event when a message is created or updated.
2. Your moderation system evaluates the message and updates the `moderation_metadata` on the message via the Discord
API to indicate what action should be taken (hide, blur, replace, etc.).
3. Discord persists the metadata and dispatches an update event to relevant game client participants via the Social SDK.
4. The Game client is notified of the update via [`Client::SetMessageUpdatedCallback`], and retrieves the
[`MessageHandle::ModerationMetadata`] from specified [`MessageHandle`] in the firing callback, and renders the message accordingly.
5. If the message content is edited, Discord clears the moderation metadata before dispatching a new update,
indicating the message needs to be re-moderated.
```mermaid theme={"system"}
sequenceDiagram
participant User as Game Client
participant SDK as Social SDK
participant Discord as Discord
participant Webhook as Game HTTP Server
participant Mod as Moderation Service
Note over User,Mod: DM Message Send & Moderation Flow
User->>SDK: Client::SendUserMessage()
SDK->>Discord: Sends message to be created
Discord->>Discord: Stores message
Discord->>SDK: Return message (unmoderated)
SDK->>User: Client::SetMessageUpdatedCallback() fires
MessageHandle.ModerationMetadata() = {}
Note left of User: Optional: Display message
with pending moderation,
or wait for moderation metadata
to be populated.
Discord->>Webhook: HTTP POST: GAME_DIRECT_MESSAGE_CREATE
Note right of Webhook: Webhook event via HTTP
Contains message content,
IDs, author info
Webhook-->>Discord: HTTP 204 (acknowledge)
Webhook->>Mod: Process message for moderation
Note right of Mod: Apply moderation rules
(toxicity check, filters, etc.)
Mod->>Webhook: Return moderation result
Note right of Mod: e.g., action: "hide"
reason: "toxicity"
rewritten: "censored text"
Webhook->>Discord: PUT /partner-sdk/dms/{user_id_1}/{user_id_2}/messages/{message_id}/moderation-metadata
Note right of Webhook: Attach moderation metadata
as key-value pairs
Discord->>Discord: Update message.moderation_metadata
Discord->>SDK: Dispatch update with moderation_metadata
Note left of Discord: No webhook event fired
SDK->>User: MessageHandle.ModerationMetadata()
has moderation data
Note left of User: Read MessageHandle.ModerationMetadata()
Render based on action (hide/show/censor)
Note over User,Mod: Message Edit Flow
User->>SDK: Client::EditUserMessage()
SDK->>Discord: Sends updated message
Discord->>Discord: Clear moderation_metadata
Note right of Discord: Stale moderation cleared
Discord->>SDK: Return updated message (unmoderated)
SDK->>User: Client::SetMessageUpdatedCallback() fires
MessageHandle.ModerationMetadata() = {}
Note left of User: Optional: Display edited message
with pending moderation,
or wait for moderation metadata
to be re-populated.
Discord->>Webhook: HTTP POST: GAME_DIRECT_MESSAGE_UPDATE
Webhook-->>Discord: HTTP 204 (acknowledge)
Webhook->>Mod: Re-moderate new content
Mod->>Webhook: New moderation result
Webhook->>Discord: PUT /partner-sdk/dms/{user_id_1}/{user_id_2}/messages/{message_id}/moderation-metadata
Discord->>Discord: Update with new metadata
Discord->>SDK: Dispatch update with new moderation_metadata
SDK->>User: SetMessageUpdatedCallback fires
MessageHandle.ModerationMetadata()
has new moderation data
Note left of User: Read metadata map
Render based on action
(hide/show/censor)
```
While this sequence diagram demonstrates moderation for direct messaging, the same flow applies to lobby messages.
See below for the appropriate Social SDK methods, Webhook Event types and API paths to use instead.
### Webhook Events
The first thing you will need to do is [subscribe to the following events](/developers/events/webhook-events#subscribing-to-events)
on your app's webhook to receive messages for moderation:
| Event | Description |
| ---------------------------- | ---------------------------------------------------- |
| `GAME_DIRECT_MESSAGE_CREATE` | Fired when a DM is created in a Social SDK session |
| `GAME_DIRECT_MESSAGE_UPDATE` | Fired when a DM is updated (content edit) |
| `LOBBY_MESSAGE_CREATE` | Fired when a message is created in a lobby |
| `LOBBY_MESSAGE_UPDATE` | Fired when a lobby message is updated (content edit) |
See the [Webhook Events reference](/developers/events/webhook-events) for full event schemas.
Metadata-only updates do **not** fire webhook events — they are delivered only via realtime SDK
callbacks to active game sessions.
### Applying Moderation Decisions
Once your moderation backend receives and evaluates a message, apply the decision by updating the `moderation_metadata`
on the message to indicate what action the game client should take with the content.
#### DM Messages
`user_id_1` and `user_id_2` are the two DM participants; order does not matter.
```python theme={"system"}
import requests
API_ENDPOINT = 'https://discord.com/api/v10'
BOT_TOKEN = 'YOUR_BOT_TOKEN'
def apply_dm_moderation(user_id_1, user_id_2, message_id, metadata):
r = requests.put(
f'{API_ENDPOINT}/partner-sdk/dms/{user_id_1}/{user_id_2}/messages/{message_id}/moderation-metadata',
headers={
'Authorization': f'Bot {BOT_TOKEN}',
'Content-Type': 'application/json',
},
json=metadata
)
r.raise_for_status()
# Instruct the client to hide this message — moderation service flagged it as toxic
metadata = {
'action': 'hide', # client will not render this message
'reason': 'toxicity', # logged by the client for reporting purposes
}
apply_dm_moderation(user_id_1, user_id_2, message_id, metadata)
# Alternatively, instruct the client to show this message — moderation service approved it
metadata = {
'action': 'show', # client will render the message normally
}
apply_dm_moderation(user_id_1, user_id_2, message_id, metadata)
```
#### Lobby Messages
```python theme={"system"}
import requests
API_ENDPOINT = 'https://discord.com/api/v10'
BOT_TOKEN = 'YOUR_BOT_TOKEN'
def apply_lobby_moderation(lobby_id, message_id, metadata):
r = requests.put(
f'{API_ENDPOINT}/lobbies/{lobby_id}/messages/{message_id}/moderation-metadata',
headers={
'Authorization': f'Bot {BOT_TOKEN}',
'Content-Type': 'application/json',
},
json=metadata
)
r.raise_for_status()
# Instruct the client to replace the message content with a policy reminder
metadata = {
'action': 'replace', # client will display replacement text instead of the original
'replacement': 'Be kind to others!', # the text the client will render in place of the message
}
apply_lobby_moderation(lobby_id, message_id, metadata)
```
Both endpoints return `HTTP 204: No Content` on success.
### Moderation Metadata Fields
The metadata body is a free-form key–value map. Use any keys your client understands.
Some common conventions might include:
| Key | Example values | Purpose |
| ------------- | --------------------------------- | ----------------------------------------------- |
| `action` | `hide`, `show`, `blur`, `replace` | How the client should render the message |
| `reason` | `toxicity`, `spam` | Why the action was taken (useful for logging) |
| `replacement` | any string | Text to display instead of the original message |
| `severity` | `low`, `medium`, `high` | Optional severity classification |
**Limits:** Up to 5 keys per message; key length ≤ 1024 characters; value length ≤ 2000 characters (the maximum
Discord message length, so values can contain modified message content).
Moderation metadata is application scoped — only your application can read it, even for messages that are visible in
Discord.
### Handling Moderation Metadata on the Client
Register [`Client::SetMessageUpdatedCallback`] to receive metadata updates. Access the metadata
through [`MessageHandle::ModerationMetadata`], which returns the key–value map written by your backend.
```cpp theme={"system"}
client->SetMessageUpdatedCallback([&client](uint64_t messageId) {
if (auto message = client->GetMessageHandle(messageId)) {
auto metadata = message->ModerationMetadata();
if (metadata.empty()) {
// No moderation decision yet — render as pending, normally, or hide completely,
// depending on your moderation requirements
renderPendingModeration(message);
return;
}
auto action = metadata.count("action") ? metadata.at("action") : "show";
if (action == "hide") {
hideMessage(messageId);
} else if (action == "show") {
renderMessage(message);
} else if (action == "replace") {
renderMessage(messageId, metadata.at("replacement"));
} else if (action == "blur") {
renderBlurred(message);
} else {
std::cerr << "Unknown moderation action: " << action << "\n";
renderMessage(message); // fall back to rendering normally
}
}
});
```
While moderation is in progress, consider not rendering new messages, or rendering in a "pending moderation"
state to avoid briefly displaying unmoderated content.
### Handling Content Edits
When a message is edited, Discord automatically clears the stored `moderation_metadata` and
dispatches a new update notification for the message.
The [`Client::SetMessageUpdatedCallback`] will fire with an empty metadata map for the edited message, and
your backend will receive a `GAME_DIRECT_MESSAGE_UPDATE` or `LOBBY_MESSAGE_UPDATE` webhook
event for the edit, which you can use to trigger re-moderation of the new content.
While re-moderation is in progress, consider not rendering the new, updated message, or rendering in a "pending
moderation" state to avoid briefly displaying unmoderated content.
## Handling Users with Banned Discord Accounts
### Discord Platform Bans
When a player's Discord account is banned by Discord — whether temporarily or permanently — they will no longer be able to authenticate to your app via Discord, their existing [`Client`] is disconnected, their OAuth2 tokens are invalidated, and an [`APPLICATION_DEAUTHORIZED`](/developers/events/webhook-events#application-deauthorized) webhook is fired.
A ban is mechanically an [unmerge](/developers/discord-social-sdk/development-guides/using-provisional-accounts#unmerging-provisional-accounts) that Discord initiates — the OAuth2 tokens are deleted and a new cross-platform-restricted provisional account takes the place of the linked Discord identity.
For the full lifecycle see [Ban-Driven Unmerge](/developers/discord-social-sdk/development-guides/using-provisional-accounts#ban-driven-unmerge) in the Provisional Accounts guide. For the auth-side observable signals and recommended integration architecture, see [Out-of-Band Revocation](/developers/discord-social-sdk/development-guides/account-linking-with-discord#out-of-band-revocation) in the Account Linking guide.
### Discord Server Bans
If you wish to tie your in-game moderation policies to a specific Discord server that you own, such as your official community server, you are able to retrieve ban information for your Discord Server via our REST APIs.
See the references for the REST endpoints[`{guild.id}/guilds/{guild.id}/bans`](/developers/resources/guild#get-guild-bans)
or [`/guilds/{guild.id}/bans/{user.id}`](/developers/resources/guild#get-guild-ban)
for more information on retrieving all bans for your guild, or ban information for a specific user within your guild.
## Voice Chat Moderation
The Discord Social SDK provides access to audio streams for in-game voice calls, allowing you to implement audio
moderation for your game's voice chat functionality. The data for the call is available through
[`Client::StartCallWithAudioCallbacks`], and can be passed to your voice moderation system.
```cpp theme={"system"}
// Example: Capturing local voice chat audio for asynchronous moderation.
// Callback for local users' audio with moderation
auto capturedCallback = [](int16_t const* data,
uint64_t samplesPerChannel, int32_t sampleRate,
uint64_t channels) {
// Call the moderation function
moderateCapturedVoice(data, samplesPerChannel);
};
// Start the call with our moderation callback
auto call = client->StartCallWithAudioCallbacks(lobbyId, receivedCallback, capturedCallback);
```
***
## Next Steps
}>
Enable private messaging between players.
}>
Add in-game voice communication.
}>
Connect game lobbies to Discord text channels.
Need help? Join the [Discord Developers Server](https://discord.gg/discord-developers) and share questions in the `#social-sdk-dev-help` channel for support from the community.
If you encounter a bug while working with the Social SDK, please report it here: [https://dis.gd/social-sdk-bug-report](https://dis.gd/social-sdk-bug-report)
***
## Change Log
| Date | Changes |
| ------------ | --------------------------------------------------------------------------- |
| May 22, 2026 | Move platform-ban lifecycle details to the Account Linking guide |
| Feb 20, 2026 | Replaced client-side message moderation with server-side message moderation |
| May 22, 2025 | initial release |
[`Client`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a91716140c699d8ef0bdf6bfd7ee0ae13
[`Client::SetMessageUpdatedCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aa01cf3c15403f29780dabfcfaf3b1dcd
[`Client::StartCallWithAudioCallbacks`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#abcaa891769f9e912bfa0e06ff7221b05
[`MessageHandle`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#ae25595b43bc74b0c4c92c5165d16382f
[`MessageHandle::ModerationMetadata`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#afb9ae126f6b1f0de006f2be8e3688205
# Market Your Integration
Source: https://docs.discord.com/developers/discord-social-sdk/how-to/market-your-integration
Best practices for marketing and promoting your Discord Social SDK integration.
## Overview
Congrats! We're so happy you've powered your game's social experiences with the Discord Social SDK, and now it's time to
let your players know about it. This toolkit is meant to provide you with guidelines and best practices to help you have
a strong go-to-market plan to make your players aware of the integration, answer their questions, and use Discord brand
guidelines appropriately.
As the developer, the choice and ownership of marketing your integration with the Discord Social SDK to your players
is yours, but we want to ensure you had a place to start if you needed. As a reminder, you may not make any statement
that suggests a partnership with, sponsorship by, or endorsement by Discord without our prior written approval in each instance.
## Announcement Goals And Messaging
Announcing **Discord Social SDK** features to your players has two goals:
* **For Players:** Drive awareness of how playing with friends is easier by linking your Discord account.
* **For Developer:** Encourage more persistent social interaction between your players.
Player-facing messaging of Discord Social SDK is different from how we frame it to developers, like you. Below are
some key messages you can pull into your announcement materials.
Our guidance focuses on features that encourage Discord account linking. It's the most important step for you and your players to experience the benefits of the integration. [Learn more about the impact of account linking](https://discord.com/developers/social-sdk).
If your announcements do not focus on Discord Social SDK features that require account linking, you can still generally use this guidance and our brand guidelines.
### A Note on the Product Name
Technically, the full product name is "Discord Social SDK". However, we recommend announcing this integration to your players with more casual language and, in most cases, sharing the full product name with players is not needed. Please follow the below guidance that provides messaging we have found resonates well with players.
If you decide to mention the full "Discord Social SDK" product name, after mentioning it once you may refer to it as the "SDK" for the remainder of your announcements.
### Key Message to Players
Link your Discord account and play with friends more easily.
### Key Benefits to Players
* **Find your friends**
* Link your Discord account to see who's playing and easily join them. From the game, invite your friends to join—even if they're offline.
* **Stay connected**
* Keep the conversation going by syncing messages and friends lists to Discord
## Detailed Feature Descriptions
In case you'd like to reference specific features and their benefits to players, use this chart.
In most cases, players won't need to know the actual “Feature Name”, but still referencing the description and use cases below are helpful for them!
| Feature Name | Short Description | Use Cases |
| --------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Rich Presence** | Shows on Discord which games players are currently playing. | Knowing what your friends are doing in-game, so you know if they'd be available to play together
Joining a game directly from a friend's profile (similar to game invites) |
| **Unified Friends List** | Players can access their Discord friends list from within the game. Includes friend tiers. | Sending game invites directly to friends from in-game
Knowing which of your friends is playing, and who isn't playing but is online and could play
Not having to re-create your friends list social in a new game that you play |
| **Deeplink Game Invites** | Users can send invites that link directly to specific game instances or parts of a game. | Challenging users to specific game instances |
| **Flexible Account Requirements** | Users can choose to link their Discord account to unlock deeper integrated features such as the Unified Friends List and Rich Presence, but can also take advantage of the in-game social features without linking an account through an innovative "provisional account." | Account linking: Automatic Discord-based sign in, or, linking of your Discord account to a game account.
Take full advantage of the Discord integration
Provisional accounts: Provisional accounts let you use Discord's voice and text chat features while playing connected games - without needing to create a Discord account first. |
| **Cross-Platform Messaging** | Allows players to communicate seamlessly both in-game and through Discord, even without a Discord account. | DMing your friends when they're in-game and out of game
Receiving messages from your friends when you aren't playing.
In-game text chat for guilds, matches, lobbies, etc. |
| **Linked Channels** | Players can link their in-game group chat with in-Discord channels, providing persistent group chats. | In-game guild/group chat mirrored in your guild's/group's Discord server
No more feeling isolated from the broader group if you're online when everyone else isn't, or vice-versa |
| **Discord Voice Chat** | High quality voice chat powered by Discord available directly in the game. | In-game voice chat for guilds, matches, lobbies, etc. |
## Integration Announcement Plan Recommendations
Below are channel recommendations for how to announce the Discord Social SDK integration in your game to your players. Please feel free to use additional channels that would resonate best with your players.
* **In-Game Messaging or Reward (Best)**
* Display a pop-up screen, banner, or CTA informing players of the recent update, prompting them to link their Discord account, and possibly reward them.
* **Announcement Video (Best)**
* Show the integration and benefits in action through a video.
* **Individual Blog Post**
* Create a blog post to outline the integration in more detail.
* **Patch Notes**
* Include the Discord integration details and images as a key update in your patch notes.
* **Social Media**
* Use other social channels to spread the word of the announcement.
* **Discord Server Announcement**
* In your main announcements channel in Discord, post a message to let your community know about these updates. Create a dedicated space for players to discuss feedback.
* **Creator Partnership**
* Partner with a creator to walk through the integration with their community.
* **Paid Media**
* Run paid ads to encourage people to link their Discord account.
* **Press**
* Pitch your story of the integration to relevant press outlets. For any press inquiries, please loop
in [press@discord.com](mailto:press@discord.com).
* **Support Ticket Setup**
* Ensure you have a new support ticket category set up so players can easily troubleshoot any problems they may encounter, such as having a “Discord account linking” category in your support tickets.
### Discord Social Handles to Tag
If you would like to tag @Discord in any of your announcements, please feel free to tag our socials below.
* X: [@discord](https://x.com/discord?lang=en)
* Instagram: [@discord](https://www.instagram.com/discord/?hl=en)
* TikTok: [@discord](https://www.tiktok.com/@discord?lang=en)
* YouTube: [@discord](https://www.youtube.com/@discord/shorts)
* LinkedIn: [@discord](https://www.linkedin.com/company/discord)
## Discord Brand Assets & Guidance
We're excited you're helping us spread the word about Discord. We'd love to make sure you have the right assets to use and guidelines from our brand team.
When using Discord Marks and Brand Assets, you must always comply with Discord's [Brand Guidelines found here](https://discord.com/branding).
As a reminder, you may not make any statement that suggests a partnership with, sponsorship by, or endorsement by Discord without our prior written approval in each instance. We also have some additional guidance below specific for your Discord Social SDK launch.
### Discord Logo and Wordmark
* [Download Link](https://discord.com/branding)
* Guidance for Discord Social SDK
* When using our logo or wordmark in your CTA buttons in-game, in social and blog post visual assets, please
review the Logo & Symbol sections of our brand guidelines. If you're not sure about use or have questions, please reach out to your Discord point of contact.
* Please refer to our articles on in-game
[Connection Points](/developers/discord-social-sdk/design-guidelines/connection-points) and
[Overall Flow](/developers/discord-social-sdk/design-guidelines/signing-in#before-the-user-connects) for more guidance for in-game messaging.
* Usage Guidance
* [Logo Usage Do's and Don'ts](https://discord.com/branding) (Click "View Brand Kit" then "Logo" then "Usage").
### Discord Bumper Splash Animation
* Downloads:
* [3 second bumper splash animation video](https://cdn.discordapp.com/assets/content/edbc1f6c8b7b0ea968888a2588ff552c2f8b2090fc2aa13e97e224427d5ad03c.mov)
* [3 second bumper splash animation audio (TV)](https://cdn.discordapp.com/assets/content/efe58ff7a83b8063a0b60f41f171932ce7d26de78fe2e2c2c531e26edadac3d4.wav)
* [3 second bumper splash animation audio (Streaming)](https://cdn.discordapp.com/assets/content/f3070170acd3f62b08ba9a63ab71080f6f7a72be5c84b74d1b247b4767696045.wav)
* Best Practice Guidance for Discord Social SDK
* This can be used as an intro splash video to your game, video launch announcement of the SDK, or game trailer
content where Discord Social SDK features are shown. You should not use this splash video in your content if Discord Social SDK features aren't present in the content.
* Usage Guidance
* When using this splash animation in your video, always include this at the beginning of the video. This splash
video should never go in the middle or end of your content.
* Using the 3 second video is preferred by Discord, but we do have 1 second and 2 second options available.
## Legal Brand Guidelines
DISCORD, DISCORD NITRO, the “Clyde” Logo and any other trademark owned by Discord and its affiliates (the “Discord Marks”)
and other brand materials such as logos, trade dress, the Discord look and feel, and other aesthetic features unique to
the brand (the “Brand Assets”) are the exclusive property of Discord Inc. You must have permission from Discord before
using any of the Discord Marks or Brand Assets except as permitted here in this document.
Please visit our [Discord Brand Guidelines](https://discord.com/branding) for additional Legal guidelines that apply to your use of Discord's Brand Assets.
***
## Change Log
| Date | Changes |
| ------------- | ---------------- |
| July 22, 2025 | initial creation |
# Use with Discord APIs
Source: https://docs.discord.com/developers/discord-social-sdk/how-to/use-with-discord-apis
Combine Discord Social SDK with Discord's REST APIs for enhanced functionality.
## Overview
The Discord Social SDK provides client-side functionality for integrating Discord social features into your game. However, if you use a game backend, you should interact with Discord's HTTP APIs for server-side operations when possible.
### Prerequisites
Before you begin, make sure you have:
* A Discord application created in the Developer Portal
* Access to your application's bot token
* Required OAuth2 scopes configured
***
## Authentication Types
### Bot Token Authentication
For server-to-server communication:
```bash theme={"system"}
curl -X GET https://discord.com/api/v10/users/@me \
-H "Authorization: Bot YOUR_BOT_TOKEN"
```
Always prefix your bot token with `Bot ` in the Authorization header!
### Bearer Token Authentication
For authenticated user actions:
```bash theme={"system"}
curl -X GET https://discord.com/api/v10/users/@me \
-H "Authorization: Bearer USER_ACCESS_TOKEN"
```
## Common API Operations
### OAuth2 Token Exchange
Exchange an authorization code for an access token:
```python theme={"system"}
# filepath: /your/backend/auth.py
import requests
def exchange_code(code, redirect_uri):
data = {
'client_id': 'YOUR_CLIENT_ID',
'client_secret': 'YOUR_CLIENT_SECRET',
'grant_type': 'authorization_code',
'code': code,
'redirect_uri': redirect_uri
}
response = requests.post('https://discord.com/api/v10/oauth2/token', data=data)
return response.json()
```
### User Information
Get information about the authenticated user:
```python theme={"system"}
def get_user_info(access_token):
headers = {'Authorization': f'Bearer {access_token}'}
response = requests.get('https://discord.com/api/v10/users/@me', headers=headers)
return response.json()
```
***
## Using the OpenAPI Specification
You can use the [Discord OpenAPI Spec](https://github.com/discord/discord-api-spec) to generate a client in your preferred backend language to make it easier to call the various APIs. A good tool for that is [https://openapi-generator.tech/](https://openapi-generator.tech/).
In python for example, that might look like this:
```python theme={"system"}
import openapi_client
import os
from pprint import pprint
configuration = openapi_client.Configuration(host = "https://discord.com/api/v10")
configuration.api_key['BotToken'] = 'Bot ' + os.environ["API_KEY"]
with openapi_client.ApiClient(configuration) as api_client:
api_instance = openapi_client.DefaultApi(api_client)
request = openapi_client.CreateLobbyRequest()
request.metadata={'foo':'bar'}
pprint(api_instance.create_lobby(request))
```
***
## Best Practices
1. Token Security
* Never expose bot tokens in client-side code
* Store tokens securely
2. Rate Limiting
* Respect Discord's [rate limits](/developers/topics/rate-limits)
* Implement exponential backoff
* Cache responses when appropriate
3. Error Handling
* Handle HTTP errors gracefully
* Implement retry logic for transient failures
* Log API errors for debugging
***
## Next Steps
Now that you've set up Rich Presence, you might want to explore:
}>
Allow players to invite friends to join their game session or party.
}>
Bring players together in a shared lobby with invites, text chat, and voice comms.
}>
Best practices for Rich Presence UI/UX.
Need help? Join the [Discord Developers Server](https://discord.gg/discord-developers) and share questions in the `#social-sdk-dev-help` channel for support from the community.
If you encounter a bug while working with the Social SDK, please report it here: [https://dis.gd/social-sdk-bug-report](https://dis.gd/social-sdk-bug-report)
***
## Change Log
| Date | Changes |
| -------------- | --------------- |
| March 17, 2025 | Initial Release |
# Voice Muting Based on Player Blocks
Source: https://docs.discord.com/developers/discord-social-sdk/how-to/voice-muting-for-blocked-players
Implement bi-directional voice muting for blocked players in lobby voice calls using lobby member
metadata, populated client-side or server-side.
This guide explains how to mute players in a lobby voice call based on block relationships — whether those blocks
come from Discord's own relationship system or from an external source such as your game or platform backend.
## Overview
This guide will help you:
* Understand why blocking a user does not automatically silence them in voice
* Populate a per-player mute list using Discord block relationships (client-side) or your game server (server-side)
* Apply that mute list using a shared implementation that works for both approaches
* Satisfy bi-directional mute requirements (if A blocks B, neither can hear the other)
## Prerequisites
Before you begin, make sure you have:
* Completed the [Getting Started Guide](/developers/discord-social-sdk/getting-started)
* A working lobby from the [Managing Lobbies](/developers/discord-social-sdk/development-guides/managing-lobbies) guide
* Familiarity with [Managing Voice Chat](/developers/discord-social-sdk/development-guides/managing-voice-chat)
* Familiarity with [Managing Relationships](/developers/discord-social-sdk/development-guides/managing-relationships)
***
## Why Voice Muting Must Be Handled Explicitly
### Discord Blocking Does Not Silence Voice
Calling [`Client::BlockUser`] prevents a user from sending friend requests or messages, but it does **not** mute them in a lobby voice call. If you want a blocked player to be inaudible, your game must explicitly call [`Call::SetLocalMute`].
### Block Signals May Come From Outside Discord
Your game may receive block information from sources other than Discord — for example, from a platform-level or game-level block list. In these cases, there is no Discord relationship to query on the client. Your server needs to supply this information to clients through another mechanism, such as lobby member metadata.
### Bi-Directional Muting
[`Call::SetLocalMute`] is one-directional: calling it on A's client only stops A from hearing B — it has no effect on what B hears. To silence audio in both directions, **both** clients must independently call `SetLocalMute` on each other.
The challenge is that both players need to independently know to mute each other. A may know to mute B based on whatever block signal they have access to — but B may not have that same information, and vice versa. Each client can only act on what it knows.
The approaches in this guide address this through lobby member metadata: each player writes their `mute_list` to their own member metadata, which is visible to all lobby members. This lets players detect when someone else has listed them and mute that person in return, regardless of the original source of the block signal.
***
## Step 1: Populate the Mute List
Both approaches in this guide work by writing a `mute_list` key to each lobby member's metadata. The value is a comma-separated list of Discord user IDs that player should mute in the voice call. How that list gets populated is what differs.
### Client-Side
Use this approach when the block information your game needs is available directly on the client — for example, from Discord's relationship system, your own in-game friend/block system, or any other client-accessible data source.
When joining the lobby, build the mute list from whatever client-side data you have and write those IDs to your own member metadata using [`Client::CreateOrJoinLobbyWithMetadata`]. This makes your mute list visible to other lobby members so they can mute you in return.
The example below uses Discord's block relationships as the data source — substitute your own logic for building `myMuteList` if your game uses a different signal.
```cpp theme={"system"}
const auto lobbySecret = "my-lobby-secret";
// Example: build the mute list from Discord block relationships.
// Replace this with your own logic if using a different data source.
std::string myMuteList;
for (const auto& rel : client->GetRelationships()) {
if (rel.DiscordRelationshipType() == discordpp::RelationshipType::Blocked) {
if (!myMuteList.empty()) myMuteList += ",";
myMuteList += std::to_string(rel.Id());
}
}
// Join the lobby with your mute list in your member metadata
client->CreateOrJoinLobbyWithMetadata(
lobbySecret,
{}, // no lobby-level metadata needed
{{"mute_list", myMuteList}},
[](const discordpp::ClientResult &result, uint64_t lobbyId) {
if (!result.Successful()) {
std::cerr << "Failed to join lobby\n";
}
}
);
```
Member metadata has a maximum total length of 1,000 characters. At \~19 characters per Discord snowflake ID plus a comma separator, this comfortably fits around 50 blocked users.
Look at [Server Side](#server-side) integration if your needs exceed this limitation.
### Server-Side
Use this approach when block information comes from an external source — such as a platform-level blocklist, when
you want to guarantee bi-directional muting without relying on clients to populate their own metadata or when you
need to filter lobby blocklists to only those in the lobby to support players who have very large potential
blocklists.
Your server code can filter each player's block list down to only the other members in the session and writes it to
their metadata when creating the lobby. Since you know exactly who is joining, you only need to consider block
relationships between those specific players — not each player's entire block list.
The client-side code in Step 2
handles the reverse direction: each client also checks whether any other participant has listed them.
```python theme={"system"}
import requests
API_ENDPOINT = 'https://discord.com/api/v10'
BOT_TOKEN = 'YOUR_BOT_TOKEN'
def create_lobby_with_mute_metadata(session_members, block_relationships):
"""
session_members: list of Discord user ID strings for this lobby
block_relationships: dict mapping user ID to list of user IDs they have blocked
Each player's mute_list contains only the session members they have blocked.
The client handles the reverse direction by checking whether others have listed them.
"""
mute_map = {user_id: set() for user_id in session_members}
# Only include blocks where both users are in this session
for blocker_id, blocked_ids in block_relationships.items():
if blocker_id in mute_map:
for blocked_id in blocked_ids:
if blocked_id in mute_map:
mute_map[blocker_id].add(blocked_id)
members = []
for user_id in session_members:
mute_list = mute_map.get(user_id, set())
members.append({
"id": user_id,
"metadata": {"mute_list": ",".join(mute_list)} if mute_list else None,
})
response = requests.post(
f'{API_ENDPOINT}/lobbies',
headers={
'Authorization': f'Bot {BOT_TOKEN}',
'Content-Type': 'application/json',
},
json={"members": members},
)
response.raise_for_status()
return response.json()
# Example: Player A has blocked Player B.
# Only A's mute_list contains B. B's mute_list is empty (B has not blocked anyone).
# The client-side code in Step 2 handles the reverse: B will detect A has listed them and mute A.
lobby = create_lobby_with_mute_metadata(
session_members=[
"111111111111111111", # Player A
"222222222222222222", # Player B
"333333333333333333", # Player C (no blocks)
],
block_relationships={
"111111111111111111": ["222222222222222222"], # A has blocked B
},
)
print(f"Lobby created: {lobby['id']}")
```
If players join the lobby after the initial creation, you may need to update their metadata with the relevant
blocklists.
Use [`POST /lobbies/{lobby.id}/members/bulk`](/developers/resources/lobby#bulk-update-lobby-members) to add or
update up to 25 members in a single request, or
[`PUT /lobbies/{lobby.id}/members/{user.id}`](/developers/resources/lobby#add-a-member-to-a-lobby) to update
a single member.
***
## Step 2: Apply the Mute List
Once the `mute_list` metadata is populated — by either approach above — the client code that reads it and applies mutes is the same.
### On Lobby Join
After joining the lobby and starting the voice call, run `ApplyMuteChecks` against every other lobby member.
```cpp theme={"system"}
const auto currentUser = client->GetCurrentUserV2();
if (!currentUser) return;
const auto myUserId = currentUser->Id();
const auto lobby = client->GetLobbyHandle(lobbyId);
auto call = client->StartCall(lobbyId);
if (lobby && call) {
for (auto memberId : lobby->LobbyMemberIds()) {
if (memberId != myUserId) {
ApplyMuteChecks(call, *lobby, myUserId, memberId);
}
}
}
// Mutes lobbyUserId locally if either myUserId or lobbyUserId has listed the other in their mute_list.
void ApplyMuteChecks(discordpp::Call& call, const discordpp::LobbyHandle& lobby,
const uint64_t myUserId, const uint64_t lobbyUserId) {
auto isListed = [&](const uint64_t ownerId, const uint64_t searchId) -> bool {
const auto member = lobby.GetLobbyMemberHandle(ownerId);
if (!member) return false;
auto metadata = member->Metadata();
const auto it = metadata.find("mute_list");
if (it == metadata.end()) return false;
std::stringstream ss(it->second);
std::string idStr;
while (std::getline(ss, idStr, ',')) {
if (std::stoull(idStr) == searchId) return true;
}
return false;
};
if (isListed(myUserId, lobbyUserId) || isListed(lobbyUserId, myUserId)) {
call.SetLocalMute(lobbyUserId, true);
}
}
```
### Handling Participants Who Join Later
Register [`Call::SetParticipantChangedCallback`] to apply mutes when new participants join the voice call mid-session.
This fires with `added = true` when someone joins and `added = false` when they leave.
```cpp theme={"system"}
call.SetParticipantChangedCallback(
[client, lobbyId, myUserId, call](uint64_t userId, const bool added) mutable {
if (!added) return;
auto lobby = client->GetLobbyHandle(lobbyId);
if (!lobby) return;
ApplyMuteChecks(call, *lobby, myUserId, userId);
}
);
```
***
## Best Practices
* **Consider filtering at matchmaking time.** The cleanest experience is to avoid placing blocked players in the same lobby at all. Voice muting handles the audio side, but blocked players may still see each other in the game UI.
* **Update mutes when relationships change.** If a player blocks someone during an active session, call [`Call::SetLocalMute`] immediately and update your `mute_list` member metadata by re-calling [`Client::CreateOrJoinLobbyWithMetadata`] with the new list. For the server-side approach, update the member's metadata via [`PUT /lobbies/{lobby.id}/members/{user.id}`](/developers/resources/lobby#add-a-member-to-a-lobby).
* **Keep metadata compact.** Lobby member metadata has a 1,000-character limit. Comma-separated ID strings are more efficient than JSON objects.
***
## Next Steps
}>
Add in-game voice communication to your lobbies.
}>
Create and manage game lobbies for matchmaking.
}>
Manage Discord user relationships including friends and blocked users.
Need help? Join the [Discord Developers Server](https://discord.gg/discord-developers) and share questions in the `#social-sdk-dev-help` channel for support from the community.
If you encounter a bug while working with the Social SDK, please report it here: [https://dis.gd/social-sdk-bug-report](https://dis.gd/social-sdk-bug-report)
***
## Change Log
| Date | Changes |
| -------------- | --------------- |
| April 22, 2026 | Initial release |
[`Call::SetLocalMute`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#aaf8e7728b15da5d1be8d8b4258225171
[`Call::SetParticipantChangedCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#acb20d338a04abec2369217f41c22c0e5
[`Client::BlockUser`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#add4a917c8382e411d5a55737c9edc8ad
[`Client::CreateOrJoinLobbyWithMetadata`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a5c84fa76c73cf3c0bfd68794ca5595c1
# Integrating the Discord Social SDK
Source: https://docs.discord.com/developers/discord-social-sdk/overview
Learn how to integrate Discord-powered social features into your game.
The Discord Social SDK lets you integrate Discord-powered social features directly into your game. For an overview of available features, see the [Social Layer for Games](/developers/platform/social-layer) platform page.
Start integrating the SDK into your game with our getting started guides, design guidelines, and code samples.
## Integrate Social Features in Your Game
}>
Learn how the SDK works and explore its key features.
}>
Follow our step-by-step guides to add the SDK to your game engine.
## Dive Deeper into the Discord Social SDK
}>
Dive into feature guides for **account linking, friends lists, game invites, and voice chat**.
}>
Learn how to design your game's UI to integrate social features.
}>
Explore the SDK's API reference documentation.
***
## Communication Features
Discord Social SDK features for text and voice communication are available but capped with rate limits.
For more information about current rate limits for communication features,
please see [Communication Features: Rate Limits](/developers/discord-social-sdk/core-concepts/communication-features#rate-limits).
For more information on how to access these features, please see [Core Concepts: Communication Features](/developers/discord-social-sdk/core-concepts/communication-features).
***
## Get Help & Join the Community
Need support or looking to connect with other game developers?
* Join our [DDevs Discord Server](https://discord.gg/discord-developers) and get help from the community, share best practices, and discover new ways to enhance your game. Find us in the `#social-sdk-dev-help` channel!
* For additional support, you can [file a ticket with Developer Support](https://dis.gd/social-sdk) to report issues.
# Social SDK Reference
Source: https://docs.discord.com/developers/discord-social-sdk/social-sdk-reference
Complete API reference for the Discord Social SDK.
# Discovery Best Practices
Source: https://docs.discord.com/developers/discovery/best-practices
Tips and best practices for optimizing your app's discovery.
So you’ve made an app on Discord and are ready to opt in to discovery on the App Directory! Or maybe you have already listed your app but aren’t seeing as much traction to it as you’d like? Whatever stage you’re at, this guide has some tips and tricks from your friendly Discord Staff members to help boost performance of your App Directory Product Page.
This guide references settings and information you can set up and modify within your [app's settings](https://discord.com/developers/applications). If you don't have an app yet, you can follow the [Getting Started guide](/developers/quick-start/getting-started).
## The Elevator Pitch
### Quickly tell your users what your app does
App descriptions should convey the value of your app and what it does. Make your app descriptions punchy and to the point - letting folks know what your app does in simple terms while also exciting the potential user to add the app and start using it.
Be sure to spell check and review your grammar before posting your descriptions. Your app has the potential to be seen by millions of people!
There are a few places where you can define different descriptions of your app within your [app's settings](https://discord.com/developers/applications).
#### App General Information Description
On the [**General Information** tab](https://discord.com/developers/applications/select/information), there is a general **Description** field (max of 400 characters) that appears within your bot user's profile. When a new user clicks on your app within Discord, they'll see this description.
#### App Summary
On the [**App Directory** tab](https://discord.com/developers/applications/select/discovery), the **Summary** field is a short description (max of 200 characters) that users will see when you appear within search results on the App Directory.
When writing your app's Summary, think about how to grab the user's attention and quickly convey the value of your app. Consider the following description:
While this could be true, it doesn’t really tell folks how the app makes servers better or more fun. The best descriptions start with an attention grabbing sentence that describes a problem a user might want to solve:
#### App Expanded Description
On the [**App Directory** tab](https://discord.com/developers/applications/select/discovery), there is an **Expanded Description** field near the bottom of the page. This description appears when someone clicks on your app and enters its profile within the App Directory. This is your opportunity to showcase why a user should install your app, and the best functionality your app has to offer. The description also supports formatting via markdown.
When writing your app's Expanded Description, the first thing you do should be to convey the value of the app to users. After the value is clear, continue on with how to get started with your app after it’s installed.
## Show Your Story
### Include as many relevant visuals as possible
#### Show Your App's Functionality
Add images and gifs to your Product Page and give users a better idea of the amazing functionality of your app. You can start by showing off some of the commands you mentioned in your description and how they work in Discord. What are some of your app’s most popular commands? How do existing users generally interact with your app? The answers to these questions make a great starting point for images on your Product Page.
Additionally, new users need to know what to expect when they install the app so great images should showcase your app in action within a Discord server. The more your images display what your app does and how it appears in Discord, the better.
#### Include Your App's Lore
Other images you may want to include are ones related to your app’s lore (if any). For example, say there was a “Hypesquad App,” I would include the Hypesquad badges and what each of them means (I’m personally Hypesquad balance myself).
#### Add a Video
After adding some images, make a quick screen recording to demonstrate your app’s functionality. Videos are awesome but it's definitely helpful to include images as well, especially for folks who may not watch the entire video.
## Find Your Users
### Utilize search to your advantage
Think of up to five words that describe your app and add them as tags (see image below for an example). Consider what categories your app would fit under or keywords users would type into the search bar when looking for apps to add to their servers.
## Support Your Users
### Engage with your users directly
Your app’s support server is a paramount part of your App Product Page. It’s important to ensure your app has a dedicated server and channel for communication between your app’s users and its developers and maintainers.
If your support server isn't [discoverable](https://support.discord.com/hc/en-us/articles/360030843331-Enabling-Server-Discovery), be sure to include an invite link in the "links" section of your App Directory Product Page.
Some ways you can successfully use your support server includes:
* Encourage users to share feedback or their experience with your app.
* Offer technical support for users of your app.
* Share app updates in your support server and update your App’s Product Page description if it changes the functionality of your app.
For general best practices on how to run a server, check out our [Community page](https://discord.com/community).
## General Housekeeping
### Final checks to make sure you’re ready to go
Before you can click the final “Enable Discovery” button, ensure that your App Directory Product Page complies with the rest of Discord’s Directory eligibility criteria, [app content requirements](https://support-dev.discord.com/hc/en-us/articles/9489299950487-App-Directory-App-Content-Requirements-Policy) and Developer [Terms of Service](https://support-dev.discord.com/hc/en-us/articles/8562894815383) and [Policy](https://support-dev.discord.com/hc/en-us/articles/8563934450327).
For more details about what this information entails, read our [Help Center article on Profile Pages](https://support-dev.discord.com/hc/en-us/articles/6378525413143-App-Directory-App-profile-pages).
Now you're ready to publish your new App Directory profile and watch your app grow! If you haven’t already, join our official [Discord Developers server](https://discord.gg/discord-developers) to receive more information on how to update your apps. Way to go, Discord Developer!
# Enabling Discovery
Source: https://docs.discord.com/developers/discovery/enabling-discovery
Step-by-step guide to verify your app and enable discovery.
Are you ready for your app to be discovered by new users and server admins?
Enabling **Discovery** for your app will make it available in the [App Directory](/developers/discovery/overview#app-directory) and [App Launcher](/developers/discovery/overview#app-launcher) for users to search for and install.
## Step 1: Verify Your App
To enable **Discovery** for your app, we require your team owner to complete identity and application verification.
**App Verification** also allows you to add [monetization features](/developers/monetization/overview) to your app, such as in-app purchases and subscriptions.
To see the list of requirements for **App Verification**:
1. Visit the [Developer Portal](https://discord.com/developers/applications) and select your app.
2. Select [App Verification](https://discord.com/developers/applications/select/verification-onboarding) from the left-hand menu to see the requirements for verification.
3. Complete the listed App Verification qualification criteria for your app.
4. Once you've completed the requirements, you can submit your app for verification.
For more information on **App Verification**, check out our [Help Center article](https://support-dev.discord.com/hc/en-us/articles/23926564536471-How-Do-I-Get-My-App-Verified).
## Step 2: Opt into Discovery
After you've verified your app, you can opt into **Discovery** in the Developer Portal, which will make your app available in the App Directory and App Launcher.
To opt into **Discovery**:
1. Visit the [Developer Portal](https://discord.com/developers/applications) and select your app.
2. Select [Discovery -> Discovery Status](https://discord.com/developers/applications/select/discovery/status) from the left-hand menu to see the requirements for enabling discovery.
3. Complete the listed **Discovery** qualification criteria for your app.
4. Add your app metadata and images under the [Discovery -> Discovery Settings](https://discord.com/developers/applications/select/discovery/settings) menu to customize your app's appearance in discovery surfaces.
5. Once you've completed the requirements, you can enable **Discovery** for your app.
For more information on **Discovery**, check out our [Help Center article](https://support-dev.discord.com/hc/en-us/articles/21204493235991-How-Can-Users-Discover-and-Play-My-Activity).
Once you enable **Discovery**, it may take up to 24 hours for your app to appear in the App Directory and App Launcher.
### Search for your App in Discord
To check if your app is discoverable, search for it in the App Directory or App Launcher.
1. Head to the [App Directory](https://discord.com/application-directory) and search for your app by name.
2. If you see it, you're good to go! 🎉
3. If not, make sure you've completed all the steps above and wait up to 24 hours for your app to appear.
***
## Next Steps
Now that you've enabled **Discovery** for your app, you can start customizing your app's appearance in the App Directory and App Launcher.
}>
Check out our guide on how to make your app stand out.
# Discovery on Discord
Source: https://docs.discord.com/developers/discovery/overview
Learn how to make your Discord app discoverable through the App Directory.
This page covers the discovery surfaces available to your app and how they work in detail. For an introduction to app discovery on Discord, see the [App Discovery](/developers/platform/discovery) platform page.
App discovery applies to [Bots](/developers/bots/overview) and [Activities](/developers/activities/overview).
## App Directory
Once you've [enabled discovery for your app](/developers/discovery/enabling-discovery), users can find it in the App Directory — a searchable hub where they can browse by name, category, or collection.
* **Search**: Users can search for your app by name and install it.
* **App Directory Product Page**: Share information about your app, including descriptions, images, videos, and links.
## App Launcher
The App Launcher lets users discover apps through collections and search from the app shapes icon throughout Discord.
### App Launcher Collections
* **Recent Apps**: Apps you've recently used or installed will appear at the top of the App Launcher
* **Installed Apps**: Apps already added to your account or server (if applicable)
* **Curated Collections**: A mix of user favorites, staff picks, and recommended apps
* **Partner Apps**: Some apps may be developed in collaboration with Discord, marked with a "Partner" tag
* **Promoted Apps**: Apps given more visibility in the App Launcher or App Directory, identified by a "Promoted" tag
Search results are ranked based on relevance to the query and popularity based on usage. Apps that use ads or offer in-app purchases are marked with "Ad-supported" and "In-App Purchases" tags on their details page.
### Editorial Recommendations
Apps can be featured in the App Directory or App Launcher as part of a curated collection or promotion. Discord's editorial team will reach out to you if we are interested in featuring your app.
***
## Social Discovery
Once your app is discoverable, users may find it through social interactions in Discord. When your app is used in a server, it may be visible to other users, allowing them to learn more about it and install it themselves from your app's profile.
### Sharing Links
Create shareable links to your app's profile page, store page, or specific items. Share these links in Discord, emails, or on your website.
### Rich Presence
Use [Rich Presence](/developers/rich-presence/overview) to show what users are doing in your app, driving more users to discover it.
***
## Next Steps
Ready to enable discovery for your app?
}>
Enable discovery for your app to make it available in the App Directory and App Launcher.
}>
Learn how to make your app stand out and drive more users to discover it.
# Gateway
Source: https://docs.discord.com/developers/events/gateway
Learn how to establish and maintain Gateway API connections with Discord.
The Gateway API lets apps open secure WebSocket connections with Discord to receive events about actions that take place in a server/guild, like when a channel is updated or a role is created. There are a few cases where apps will *also* use Gateway connections to update or request resources, like when updating voice state.
In *most* cases, performing REST operations on Discord resources can be done using the [HTTP API](/developers/reference#http-api) rather than the Gateway API.
The Gateway is Discord's form of real-time communication used by clients (including apps), so there are nuances and data passed that simply isn't relevant to apps. Interacting with the Gateway can be tricky, but there are [community-built libraries](/developers/developer-tools/community-resources#libraries) with built-in support that simplify the most complicated bits and pieces. If you're planning on writing a custom implementation, be sure to read the following documentation in its entirety so you understand the sacred secrets of the Gateway (or at least those that matter for apps).
## Gateway Events
Gateway events are [payloads](/developers/events/gateway-events#payload-structure) sent over a [Gateway connection](/developers/events/gateway#connections)—either from an app to Discord, or from Discord to an app. An app typically [*sends* events](/developers/events/gateway#sending-events) when connecting and managing its connection to the Gateway, and [*receives* events](/developers/events/gateway#receiving-events) when listening to actions taking place in a server.
All Gateway events are encapsulated in a [Gateway event payload](/developers/events/gateway-events#payload-structure).
A full list of Gateway events and their details are in the [Gateway events documentation](/developers/events/gateway-events).
###### Example Gateway Event
```json theme={"system"}
{
"op": 0,
"d": {},
"s": 42,
"t": "GATEWAY_EVENT_NAME"
}
```
Details about Gateway event payloads are in the [Gateway events documentation](/developers/events/gateway-events#payload-structure).
### Sending Events
When sending a Gateway event (like when [performing an initial handshake](/developers/events/gateway-events#identify) or [updating presence](/developers/events/gateway-events#update-presence)), your app must send an [event payload object](/developers/events/gateway-events#payload-structure) with a valid opcode (`op`) and inner data object (`d`).
Specific rate limits are applied when sending events, which you can read about in the [Rate Limiting](/developers/events/gateway#rate-limiting) section.
Event payloads sent over a Gateway connection:
1. Must be serialized in [plain-text JSON or binary ETF](/developers/events/gateway#encoding-and-compression).
2. Must not exceed 4096 bytes. If an event payload *does* exceed 4096 bytes, the connection will be closed with a [`4002` close event code](/developers/topics/opcodes-and-status-codes#gateway-gateway-close-event-codes).
All events that your app can send via a connection are in [Gateway event documentation](/developers/events/gateway-events#send-events).
### Receiving Events
Receiving a Gateway event from Discord (like when [a reaction is added to a message](/developers/events/gateway-events#message-reaction-add)) is much more common (and slightly more complex) than sending them.
While some events are sent to your app automatically, most events require your app to define intents when [Identifying](/developers/events/gateway#identifying). Intents are bitwise values that can be ORed (`|`) to indicate which events (or groups of events) you want Discord to send your app. A list of intents and their corresponding events are listed in the [intents section](/developers/events/gateway#gateway-intents).
When receiving events, you can also configure *how* events will be sent to your app, like the [encoding and compression](/developers/events/gateway#encoding-and-compression), or whether [sharding should be enabled](/developers/events/gateway#sharding)).
All events that your app can receive via a connection are in the [Gateway event documentation](/developers/events/gateway-events#receive-events).
#### Dispatch Events
[Dispatch (opcode `0`)](/developers/topics/opcodes-and-status-codes#gateway-gateway-opcodes) events are the most common type of event your app will receive. *Most* Gateway events which represent actions taking place in a guild will be sent to your app as Dispatch events.
When your app is parsing a Dispatch event:
* The `t` field can be used to determine which [Gateway event](/developers/events/gateway-events#receive-events) the payload represents the data you can expect in the `d` field.
* The `s` field represents the sequence number of the event, which is the relative order in which it occurred. You need to cache the most recent non-null `s` value for heartbeats, and to pass when [Resuming](/developers/events/gateway#resuming) a connection.
## Connections
Gateway connections are persistent WebSockets which introduce more complexity than sending HTTP requests or responding to interactions (like Slash Commands). When interacting with the Gateway, your app must know how to open the initial connection, as well as maintain it and handle any disconnects.
### Connection Lifecycle
There are nuances that aren't included in the overview below. More details about each step and event can be found in the individual sections below.
At a high-level, Gateway connections consist of the following cycle:
1. App establishes a connection with the Gateway after fetching and caching a WSS URL using the [Get Gateway](/developers/events/gateway#get-gateway) or [Get Gateway Bot](/developers/events/gateway#get-gateway-bot) endpoint.
2. Discord sends the app a [Hello (opcode `10`)](/developers/events/gateway#hello-event) event containing a heartbeat interval in milliseconds. **Read the section on [Connecting](/developers/events/gateway#connecting)**
3. Start the Heartbeat interval. App must send a [Heartbeat (opcode `1`)](/developers/events/gateway-events#heartbeat) event, then continue to send them every heartbeat interval until the connection is closed. **Read the section on [Sending Heartbeats](/developers/events/gateway#sending-heartbeats)**
* Discord will respond to each Heartbeat event with a [Heartbeat ACK (opcode `11`)](/developers/events/gateway#sending-heartbeats) event to confirm it was received. If an app doesn't receive a Heartbeat ACK, it should close the connection and reconnect.
* Discord may send the app a [Heartbeat (opcode `1`)](/developers/events/gateway-events#heartbeat) event, in which case the app should send a Heartbeat event immediately.
4. App sends an [Identify (opcode `2`)](/developers/events/gateway-events#identify) event to perform the initial handshake with the Gateway. **Read the section on [Identifying](/developers/events/gateway#identifying)**
5. Discord sends the app a [Ready (opcode `0`)](/developers/events/gateway-events#ready) event which indicates the handshake was successful and the connection is established. The Ready event contains a `resume_gateway_url` that the app should keep track of to determine the WebSocket URL an app should use to Resume. **Read the section on [the Ready event](/developers/events/gateway#ready-event)**
6. The connection may be dropped for a variety of reasons at any time. Whether the app can [Resume](/developers/events/gateway#resuming) the connection or whether it must re-identify is determined by a variety of factors like the [opcode](/developers/topics/opcodes-and-status-codes#gateway-gateway-opcodes) and [close code](/developers/topics/opcodes-and-status-codes#gateway-gateway-close-event-codes) that it receives. **Read the section on [Disconnecting](/developers/events/gateway#disconnecting)**
7. If an app **can** resume/reconnect, it should open a new connection using `resume_gateway_url` with the same version and encoding, then send a [Resume (opcode `6`)](/developers/events/gateway-events#resume) event. If an app **cannot** resume/reconnect, it should open a new connection using the cached URL from step #1, then repeat the whole Gateway cycle. *Yipee!* **Read the section on [Resuming](/developers/events/gateway#resuming)**
### Connecting
Before your app can establish a connection to the Gateway, it should call the [Get Gateway](/developers/events/gateway#get-gateway) or the [Get Gateway Bot](/developers/events/gateway#get-gateway-bot) endpoint. Either endpoint will return a payload with a `url` field whose value is the WSS URL you can use to open a WebSocket connection. In addition to the URL, [Get Gateway Bot](/developers/events/gateway#get-gateway-bot) contains additional information about the recommended number of shards and the session start limits for your app.
When initially calling either [Get Gateway](/developers/events/gateway#get-gateway) or [Get Gateway Bot](/developers/events/gateway#get-gateway-bot), you should cache the value of the `url` field and use that when re-connecting to the Gateway.
When connecting to the URL, it's a good idea to explicitly pass the API version and [encoding](/developers/events/gateway#encoding-and-compression) as query parameters. You can also optionally include whether Discord should [compress](/developers/events/gateway#encoding-and-compression) data that it sends your app.
`wss://gateway.discord.gg/?v=10&encoding=json` is an example of a WSS URL an app may use to connect to the Gateway.
###### Gateway URL Query String Params
| Field | Type | Description | Accepted Values |
| --------- | ------- | --------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| v | integer | [API Version](/developers/reference#api-versioning) to use | [API version](/developers/reference#api-versioning-api-versions) |
| encoding | string | The [encoding](/developers/events/gateway#encoding-and-compression) of received gateway packets | `json` or `etf` |
| compress? | string | The optional [transport compression](/developers/events/gateway#transport-compression) of gateway packets | `zlib-stream` or `zstd-stream` |
#### Hello Event
Once connected to the Gateway, your app will receive a [Hello (opcode `10`)](/developers/events/gateway#hello-event) event that contains your connection's heartbeat interval (`heartbeat_interval`).
The heartbeat interval indicates a length of time in milliseconds that you should use to determine how often your app needs to send a Heartbeat event in order to maintain the active connection. Heartbeating is detailed in the [Sending Heartbeats](/developers/events/gateway#sending-heartbeats) section.
###### Example Hello Event
```json theme={"system"}
{
"op": 10,
"d": {
"heartbeat_interval": 45000
}
}
```
### Sending Heartbeats
Heartbeats are pings used to let Discord know that your app is still actively using a Gateway connection. After connecting to the Gateway, your app should send heartbeats (as described below) in a background process until the Gateway connection is closed.
#### Heartbeat Interval
When your app opens a Gateway connection, it will receive a [Hello (opcode `10`)](/developers/events/gateway#hello-event) event which includes a `heartbeat_interval` field that has a value representing a length of time in milliseconds.
Upon receiving the Hello event, your app should wait `heartbeat_interval * jitter` where `jitter` is any random value between 0 and 1, then send its first [Heartbeat (opcode `1`)](/developers/events/gateway-events#heartbeat) event. From that point until the connection is closed, your app must continually send Discord a heartbeat every `heartbeat_interval` milliseconds. If your app fails to send a heartbeat event in time, your connection will be closed and you will be forced to [Resume](/developers/events/gateway#resuming).
When sending a heartbeat, your app will need to include the last sequence number your app received in the `d` field. The sequence number is sent to your app in the [event payload](/developers/events/gateway-events#payload-structure) in the `s` field. If your app hasn't received any events yet, you can just pass `null` in the `d` field.
In the first heartbeat, `jitter` is an offset value between 0 and `heartbeat_interval` that is meant to prevent too many clients (both desktop and apps) from reconnecting their sessions at the exact same time (which could cause an influx of traffic).
You *can* send heartbeats before the `heartbeat_interval` elapses, but you should avoid doing so unless necessary. There is already tolerance in the `heartbeat_interval` that will cover network latency, so you don't need to account for it in your implementation.
When you send a Heartbeat event, Discord will respond with a [Heartbeat ACK (opcode `11`)](/developers/events/gateway#heartbeat-interval-example-heartbeat-ack) event, which is an acknowledgement that the heartbeat was received:
###### Example Heartbeat ACK
```json theme={"system"}
{
"op": 11
}
```
In the event of a service outage where you stay connected to the Gateway, you should continue to send heartbeats and receive heartbeat ACKs. The Gateway will eventually respond and issue a session once it's able to.
If a client does not receive a heartbeat ACK between its attempts at sending heartbeats, this may be due to a failed or "zombied" connection. The client should immediately terminate the connection with any close code besides `1000` or `1001`, then reconnect and attempt to [Resume](/developers/events/gateway#resuming).
#### Heartbeat Requests
In addition to the Heartbeat interval, Discord may request additional heartbeats from your app by sending a [Heartbeat (opcode `1`)](/developers/events/gateway-events#heartbeat) event. Upon receiving the event, your app should immediately send back another Heartbeat event without waiting the remainder of the current interval.
Just like with the interval, Discord will respond with an [Heartbeat ACK (opcode `11`)](/developers/events/gateway#heartbeat-interval-example-heartbeat-ack) event.
### Identifying
After the connection is open and your app is sending heartbeats, you should send an [Identify (opcode `2`)](/developers/events/gateway-events#identify) event. The Identify event is an initial handshake with the Gateway that's required before your app can begin sending or receiving most Gateway events.
Apps are limited by maximum concurrency (`max_concurrency` in the [session start limit object](/developers/events/gateway#session-start-limit-object)) when identifying. If your app exceeds this limit, Discord will respond with a [Invalid Session (opcode `9`)](/developers/events/gateway-events#invalid-session) event.
After your app sends a valid Identify payload, Discord will respond with a [Ready](/developers/events/gateway-events#ready) event which indicates that your app is in a successfully-connected state with the Gateway. The Ready event is sent as a standard [Dispatch (opcode `0`)](/developers/topics/opcodes-and-status-codes#gateway-gateway-opcodes).
Clients are limited to 1000 `IDENTIFY` calls to the websocket in a 24-hour period. This limit is global and across all shards, but does not include `RESUME` calls. Upon hitting this limit, all active sessions for the app will be terminated, the bot token will be reset, and the owner will receive an email notification. It's up to the owner to update their application with the new token.
###### Example Identify Payload
Below is a minimal `IDENTIFY` payload. `IDENTIFY` supports additional fields for other session properties like payload compression and an initial presence state.
See the [Identify Structure](/developers/events/gateway-events#identify-identify-structure) for details about the event.
```json theme={"system"}
{
"op": 2,
"d": {
"token": "my_token",
"intents": 513,
"properties": {
"os": "linux",
"browser": "my_library",
"device": "my_library"
}
}
}
```
#### Ready event
As mentioned above, the [Ready](/developers/events/gateway-events#ready) event is sent to your app after it sends a valid Identify payload. The Ready event includes state, like the guilds your app is in, that it needs to start interacting with the rest of the platform.
The Ready event also includes fields that you'll need to cache in order to eventually [Resume](/developers/events/gateway#resuming) your connection after disconnects. Two fields in particular are important to call out:
* `resume_gateway_url` is a WebSocket URL that your app should use when it Resumes after a disconnect. The `resume_gateway_url` should be used instead of the URL [used when connecting](/developers/events/gateway#connecting).
* `session_id` is the ID for the Gateway session for the new connection. It's required to know which stream of events were associated with your disconnection connection.
Full details about the Ready event is in the [Gateway events documentation](/developers/events/gateway-events).
### Disconnecting
Gateway disconnects happen for a variety of reasons, and may be initiated by Discord or by your app.
#### Handling a Disconnect
Due to Discord's architecture, disconnects are a semi-regular event and should be expected and handled. When your app encounters a disconnect, it will typically be sent a [close code](/developers/topics/opcodes-and-status-codes#gateway-gateway-close-event-codes) which can be used to determine whether you can reconnect and [Resume](/developers/events/gateway#resuming) the session, or whether you have to start over and re-Identify.
After you determine whether or not your app can reconnect, you will do one of the following:
* If you determine that your app *can* reconnect and resume the previous session, then you should reconnect using the `resume_gateway_url` and `session_id` from the [Ready](/developers/events/gateway-events#ready) event. Details about when and how to resume can be found in the [Resuming](/developers/events/gateway#resuming) section.
* If you *cannot* reconnect **or the reconnect fails**, you should open a new connection using the URL from the initial call to [Get Gateway](/developers/events/gateway#get-gateway) or [Get Gateway Bot](/developers/events/gateway#get-gateway-bot). In the case you cannot reconnect, you'll have to re-identify after opening a new connection.
A full list of the close codes can be found in the [Response Codes](/developers/topics/opcodes-and-status-codes#gateway-gateway-close-event-codes) documentation.
#### Initiating a Disconnect
When you close the connection to the gateway with close code `1000` or `1001`, your session will be invalidated and your bot will appear offline.
If you simply close the TCP connection or use a different close code, the session will remain active and timeout after a few minutes. This can be useful when you're [Resuming](/developers/events/gateway#resuming) the previous session.
### Resuming
When your app is disconnected, Discord has a process for reconnecting and resuming, which allows your app to replay any lost events starting from the last sequence number it received. After Resuming, your app will receive the missed events in the same way it would have had the connection had stayed active. Unlike the initial connection, your app does **not** need to re-Identify when Resuming.
There are a handful of scenarios when your app should attempt to resume:
1. It receives a [Reconnect (opcode `7`)](/developers/events/gateway-events#reconnect) event
2. It's disconnected with a close code that indicates it can reconnect. A list of close codes is in the [Opcodes and Status Codes](/developers/topics/opcodes-and-status-codes#gateway-gateway-close-event-codes) documentation.
3. It's disconnected but doesn't receive *any* close code.
4. It receives an [Invalid Session (opcode `9`)](/developers/events/gateway-events#invalid-session) event with the `d` field set to `true`. This is an unlikely scenario, but it is possible.
#### Preparing to Resume
Before your app can send a [Resume (opcode `6`)](/developers/events/gateway-events#resume) event, it will need three values: the `session_id` and the `resume_gateway_url` from the [Ready](/developers/events/gateway#ready-event) event, and the sequence number (`s`) from the last Dispatch (opcode `0`) event it received before the disconnect.
After the connection is closed, your app should open a new connection using `resume_gateway_url` rather than the URL used to initially connect, with the same query parameters from the initial [Connection](/developers/events/gateway#connecting). If your app doesn't use the `resume_gateway_url` when reconnecting, it will experience disconnects at a higher rate than normal.
Once the new connection is opened, your app should send a [Gateway Resume](/developers/events/gateway-events#resume) event using the `session_id` and sequence number mentioned above. When sending the event, `session_id` will have the same field name, but the last sequence number will be passed as `seq` in the data object (`d`).
When Resuming, you do not need to send an Identify event after opening the connection.
If successful, the Gateway will send the missed events in order, finishing with a [Resumed](/developers/events/gateway-events#resumed) event to signal event replay has finished and that all subsequent events will be new.
When resuming with the `resume_gateway_url` you need to provide the same version and encoding as the initial connection.
It's possible your app won't reconnect in time to Resume, in which case it will receive an [Invalid Session (opcode `9`)](/developers/events/gateway-events#invalid-session) event. If the `d` field is set to `false` (which is most of the time), your app should disconnect. After disconnect, your app should create a new connection with your cached URL from the [Get Gateway](/developers/events/gateway#get-gateway) or the [Get Gateway Bot](/developers/events/gateway#get-gateway-bot) endpoint, then send an [Identify (opcode `2`)](/developers/events/gateway-events#identify) event.
###### Example Gateway Resume Event
```json theme={"system"}
{
"op": 6,
"d": {
"token": "my_token",
"session_id": "session_id_i_stored",
"seq": 1337
}
}
```
## Gateway Intents
Maintaining a stateful application can be difficult when it comes to the amount of data your app is expected to process over a Gateway connection, especially at scale. Gateway intents are a system to help you lower the computational burden.
Intents are bitwise values passed in the `intents` parameter when [Identifying](/developers/events/gateway#identifying) which correlate to a set of related events. For example, the event sent when a guild is created (`GUILD_CREATE`) and when a channel is updated (`CHANNEL_UPDATE`) both require the same `GUILDS (1 << 0)` intent (as listed in the table below). If you do not specify an intent when identifying, you will not receive *any* of the Gateway events associated with that intent.
Intents are optionally supported on the v6 gateway but required as of v8
Two types of intents exist:
* **Standard intents** can be passed by default. You don't need any additional permissions or configurations.
* [**Privileged intents**](/developers/events/gateway#privileged-intents) require you to toggle the intent for your app in your app's settings within the Developer Portal before passing said intent. For verified apps (required for apps in 100+ guilds), the intent must also be approved after the verification process to use the intent. More information about privileged intents can be found [in the section below](/developers/events/gateway#privileged-intents).
The connection with your app will be closed if it passes invalid intents ([`4013` close code](/developers/topics/opcodes-and-status-codes#gateway-gateway-close-event-codes)), or a privileged intent that hasn't been configured or approved for your app ([`4014` close code](/developers/topics/opcodes-and-status-codes#gateway-gateway-close-event-codes)).
### List of Intents
Below is a list of all intents and the Gateway events associated with them. Any events *not* listed means it's not associated with an intent and will always be sent to your app.
All events, including those that aren't associated with an intent, are in the [Gateway events](/developers/events/gateway-events) documentation.
```
GUILDS (1 << 0)
- GUILD_CREATE
- GUILD_UPDATE
- GUILD_DELETE
- GUILD_ROLE_CREATE
- GUILD_ROLE_UPDATE
- GUILD_ROLE_DELETE
- CHANNEL_CREATE
- CHANNEL_UPDATE
- CHANNEL_DELETE
- CHANNEL_PINS_UPDATE
- THREAD_CREATE
- THREAD_UPDATE
- THREAD_DELETE
- THREAD_LIST_SYNC
- THREAD_MEMBER_UPDATE
- THREAD_MEMBERS_UPDATE *
- STAGE_INSTANCE_CREATE
- STAGE_INSTANCE_UPDATE
- STAGE_INSTANCE_DELETE
- VOICE_CHANNEL_STATUS_UPDATE
- VOICE_CHANNEL_START_TIME_UPDATE
GUILD_MEMBERS (1 << 1) **
- GUILD_MEMBER_ADD
- GUILD_MEMBER_UPDATE
- GUILD_MEMBER_REMOVE
- THREAD_MEMBERS_UPDATE *
GUILD_MODERATION (1 << 2)
- GUILD_AUDIT_LOG_ENTRY_CREATE
- GUILD_BAN_ADD
- GUILD_BAN_REMOVE
GUILD_EXPRESSIONS (1 << 3)
- GUILD_EMOJIS_UPDATE
- GUILD_STICKERS_UPDATE
- GUILD_SOUNDBOARD_SOUND_CREATE
- GUILD_SOUNDBOARD_SOUND_UPDATE
- GUILD_SOUNDBOARD_SOUND_DELETE
- GUILD_SOUNDBOARD_SOUNDS_UPDATE
GUILD_INTEGRATIONS (1 << 4)
- GUILD_INTEGRATIONS_UPDATE
- INTEGRATION_CREATE
- INTEGRATION_UPDATE
- INTEGRATION_DELETE
GUILD_WEBHOOKS (1 << 5)
- WEBHOOKS_UPDATE
GUILD_INVITES (1 << 6)
- INVITE_CREATE
- INVITE_DELETE
GUILD_VOICE_STATES (1 << 7)
- VOICE_CHANNEL_EFFECT_SEND
- VOICE_STATE_UPDATE
GUILD_PRESENCES (1 << 8) **
- PRESENCE_UPDATE
GUILD_MESSAGES (1 << 9)
- MESSAGE_CREATE
- MESSAGE_UPDATE
- MESSAGE_DELETE
- MESSAGE_DELETE_BULK
GUILD_MESSAGE_REACTIONS (1 << 10)
- MESSAGE_REACTION_ADD
- MESSAGE_REACTION_REMOVE
- MESSAGE_REACTION_REMOVE_ALL
- MESSAGE_REACTION_REMOVE_EMOJI
GUILD_MESSAGE_TYPING (1 << 11)
- TYPING_START
DIRECT_MESSAGES (1 << 12)
- MESSAGE_CREATE
- MESSAGE_UPDATE
- MESSAGE_DELETE
- CHANNEL_PINS_UPDATE
DIRECT_MESSAGE_REACTIONS (1 << 13)
- MESSAGE_REACTION_ADD
- MESSAGE_REACTION_REMOVE
- MESSAGE_REACTION_REMOVE_ALL
- MESSAGE_REACTION_REMOVE_EMOJI
DIRECT_MESSAGE_TYPING (1 << 14)
- TYPING_START
MESSAGE_CONTENT (1 << 15) ***
GUILD_SCHEDULED_EVENTS (1 << 16)
- GUILD_SCHEDULED_EVENT_CREATE
- GUILD_SCHEDULED_EVENT_UPDATE
- GUILD_SCHEDULED_EVENT_DELETE
- GUILD_SCHEDULED_EVENT_USER_ADD
- GUILD_SCHEDULED_EVENT_USER_REMOVE
AUTO_MODERATION_CONFIGURATION (1 << 20)
- AUTO_MODERATION_RULE_CREATE
- AUTO_MODERATION_RULE_UPDATE
- AUTO_MODERATION_RULE_DELETE
AUTO_MODERATION_EXECUTION (1 << 21)
- AUTO_MODERATION_ACTION_EXECUTION
GUILD_MESSAGE_POLLS (1 << 24)
- MESSAGE_POLL_VOTE_ADD
- MESSAGE_POLL_VOTE_REMOVE
DIRECT_MESSAGE_POLLS (1 << 25)
- MESSAGE_POLL_VOTE_ADD
- MESSAGE_POLL_VOTE_REMOVE
```
\* [Thread Members Update](/developers/events/gateway-events#thread-members-update) contains different data depending on which intents are used.
\*\* Events under the `GUILD_PRESENCES` and `GUILD_MEMBERS` intents are turned **off by default on all API versions**. If you are using **API v6**, you will receive those events if you are authorized to receive them and have enabled the intents in the Developer Portal. You do not need to use intents on API v6 to receive these events; you just need to enable the flags. If you are using **API v8** or above, intents are mandatory and must be specified when identifying.
\*\*\* `MESSAGE_CONTENT` does not represent individual events, but rather affects what data is present for events that could contain message content fields. More information is in the [message content intent](/developers/events/gateway#message-content-intent) section.
### Caveats
[Guild Member Update](/developers/events/gateway-events#guild-member-update) is sent for current-user updates regardless of whether the `GUILD_MEMBERS` intent is set.
[Guild Create](/developers/events/gateway-events#guild-create) and [Request Guild Members](/developers/events/gateway-events#request-guild-members) are uniquely affected by intents. See these sections for more information.
[Thread Members Update](/developers/events/gateway-events#thread-members-update) by default only includes if the current user was added to or removed from a thread. To receive these updates for other users, request the `GUILD_MEMBERS` [Gateway Intent](/developers/events/gateway#gateway-intents).
### Privileged Intents
Some intents have additional requirements for access due to the nature of their data. Currently, those intents include:
* `GUILD_PRESENCES`
* `GUILD_MEMBERS`
* [`MESSAGE_CONTENT`](/developers/events/gateway#message-content-intent)
Apps that qualify for verification **must** be approved for the privileged intent(s) before they can use them. After your app is verified, you can request privileged intents within the app's settings within the Developer Portal.
Before you can specify any of these privileged intents in your `IDENTIFY` payload, you must enable the specific privileged intents you need in the Developer Portal.
You may only enable a privileged intent if your use case requires it to function and is consistent with our Developer Policy. Check out our [You Might Not Need a Privileged Intent](/developers/gateway/you-might-not-need-a-privileged-intent) guide to better understand what data these intents grant and alternative approaches to consider when building your app.
#### Enabling Privileged Intents
Before using privileged intents, you must enable them in your app's settings. In the Developer Portal, you can navigate to your app's settings, then toggle the privileged intents on the [**Bot** page](https://discord.com/developers/applications/select/bot) under the "Privileged Gateway Intents" section. You may only toggle privileged intents that your bot requires to function.
#### Privileged Intent Access Review
Apps with fewer than 10,000 users can access privileged intents by enabling them in the Developer Portal.
When an app has more than 10,000 unique users who can see your app across all the servers it's in, it requires review for continued access to Privileged Intents. Once you hit this threshold, the app or team owner will receive a system DM and/or an email. There will also be a callout visible on your app in the Developer Portal. You will need to submit a request with information about your app and its use of Privileged Intents for review. If access is granted, you can enable the relevant intents for your app in the Developer Portal and you will be notified annually to reapply for continued access.
Read our [Getting Started with Privileged Intent Review](/developers/gateway/getting-started-with-privileged-intent-review) guide to learn more about the user threshold, requirements, and review process.
#### Gateway Restrictions
Privileged intents affect which Gateway events your app is permitted to receive. When using **API v8** and above, all intents (privileged and not) must be specified in the `intents` parameter when Identifying. If you pass a privileged intent in the `intents` parameter without configuring it in your app's settings, or being approved for it during verification, your Gateway connection will be closed with a ([`4014` close code](/developers/topics/opcodes-and-status-codes#gateway-gateway-close-event-codes)).
For **API v6**, you will receive events associated with the privileged intents your app has configured and is authorized to receive *without* passing those intents into the `intents` parameter when Identifying.
Events associated with the `GUILD_PRESENCES` and `GUILD_MEMBERS` intents are turned off by default regardless of the API version.
#### HTTP Restrictions
In addition to Gateway restrictions, privileged intents also affect the [HTTP API](/developers/reference#http-api) endpoints your app is permitted to call, and the data it can receive. For example, to use the [List Guild Members](/developers/resources/guild#list-guild-members) endpoint, your app must enable the `GUILD_MEMBERS` intent (and be approved for it if eligible for verification).
HTTP API restrictions are independent of Gateway restrictions, and are unaffected by which intents your app passes in the `intents` parameter when Identifying.
#### Message Content Intent
`MESSAGE_CONTENT (1 << 15)` is a unique privileged intent that isn't directly associated with any Gateway events. Instead, access to `MESSAGE_CONTENT` permits your app to receive message content data across the APIs.
Any fields affected by the message content intent are noted in the relevant documentation. For example, the `content`, `embeds`, `attachments`, `components`, and `poll` fields in [message objects](/developers/resources/message#message-object) all contain message content and therefore require the intent.
Like other privileged intents, `MESSAGE_CONTENT` must be approved for your app. After your app is verified, you can apply for the intent from your app's settings within the Developer Portal. You can read more about the message content intent review policy [in the Help Center](https://support-dev.discord.com/hc/en-us/articles/5324827539479).
Apps **without** the intent will receive empty values in fields that contain user-inputted content with a few exceptions:
* Content in messages that an app sends
* Content in DMs with the app
* Content in which the app is [mentioned](/developers/reference#message-formatting-formats)
* Content of the message a [message context menu command](/developers/interactions/application-commands#message-commands) is used on
## Rate Limiting
This section refers to Gateway rate limits, not [HTTP API rate limits](/developers/topics/rate-limits)
Apps can send 120 [gateway events](/developers/events/gateway-events) per [connection](/developers/events/gateway#connections) every 60 seconds, meaning an average of 2 commands per second. Apps that surpass the limit are immediately disconnected from the Gateway. Similar to other rate limits, repeat offenders will have their API access revoked.
Apps also have a limit for [concurrent](/developers/events/gateway#session-start-limit-object) [Identify](/developers/events/gateway#identifying) requests allowed per 5 seconds. If you hit this limit, the Gateway will respond with an [Invalid Session (opcode `9`)](/developers/events/gateway-events#invalid-session).
## Encoding and Compression
When [establishing a connection](/developers/events/gateway#connecting) to the Gateway, apps can use the `encoding` parameter to choose whether to communicate with Discord using a plain-text JSON or binary [ETF](https://erlang.org/doc/apps/erts/erl_ext_dist.html) encoding. You can pick whichever encoding type you're more comfortable with, but both have their own quirks. If you aren't sure which encoding to use, JSON is generally recommended.
Apps can also optionally enable compression to receive zlib-compressed or zstd-compressed packets. [Payload compression](/developers/events/gateway#payload-compression) can only be enabled when using a JSON encoding, but [transport compression](/developers/events/gateway#transport-compression) can be used regardless of encoding type.
### Using JSON Encoding
When using the plain-text JSON encoding, apps have the option to enable [payload compression](/developers/events/gateway#payload-compression).
#### Payload Compression
If an app is using payload compression, it cannot use [transport compression](/developers/events/gateway#transport-compression).
Payload compression enables optional per-packet compression for *some* events when Discord is sending events over the connection.
Payload compression uses the zlib format (see [RFC1950 2.2](https://tools.ietf.org/html/rfc1950#section-2.2)) when sending payloads. To enable payload compression, your app can set `compress` to `true` when sending an [Identify (opcode `2`)](/developers/events/gateway-events#identify) event. Note that even when payload compression is enabled, not all payloads will be compressed.
When payload compression is enabled, your app (or library) *must* detect and decompress these payloads to plain-text JSON before attempting to parse them. If you are using payload compression, the gateway does not implement a shared compression context between events sent.
Payload compression will be disabled if you use [transport compression](/developers/events/gateway#transport-compression).
### Using ETF Encoding
When using ETF (External Term Format) encoding, there are some specific behaviors you should know:
* Snowflake IDs are transmitted as 64-bit integers or strings.
* Your app can't send compressed messages to the server.
* When sending payloads, you must use string keys. Using atom keys will result in a [`4002`](/developers/topics/opcodes-and-status-codes#gateway-gateway-close-event-codes) decode error.
See [erlpack](https://github.com/discord/erlpack) for an ETF implementation example.
### Transport Compression
Transport compression enables optional compression for all packets when Discord is sending events over the connection. The currently-available transport compression options are `zlib-stream` and `zstd-stream`.
#### zlib-stream
When zlib transport compression is enabled, your app needs to process received data through a single Gateway connection using a shared zlib context. However, each Gateway connection should use its own unique zlib context.
When processing transport-compressed data, you should push received data to a buffer until you receive the 4-byte `Z_SYNC_FLUSH` suffix (`00 00 ff ff`). After you receive the `Z_SYNC_FLUSH` suffix, you can then decompress the buffer.
###### Transport Compression Example
```python theme={"system"}
# Z_SYNC_FLUSH suffix
ZLIB_SUFFIX = b'\x00\x00\xff\xff'
# initialize a buffer to store chunks
buffer = bytearray()
# create a shared zlib inflation context to run chunks through
inflator = zlib.decompressobj()
# ...
def on_websocket_message(msg):
# always push the message data to your cache
buffer.extend(msg)
# check if the last four bytes are equal to ZLIB_SUFFIX
if len(msg) < 4 or msg[-4:] != ZLIB_SUFFIX:
return
# if the message *does* end with ZLIB_SUFFIX,
# get the full message by decompressing the buffers
# NOTE: the message is utf-8 encoded.
msg = inflator.decompress(buffer)
buffer = bytearray()
# here you can treat `msg` as either JSON or ETF encoded,
# depending on your `encoding` param
```
#### zstd-stream
When zstd-stream transport compression is enabled, all data needs to be processed through a zstd decompression context that stays alive for the lifetime of the gateway connection.
When processing data, each websocket message corresponds to a single gateway message, but does not end a zstd frame. You will need to repeatedly call ZSTD\_decompressStream until all data in the frame has been processed (ZSTD\_decompressStream will not necessarily return 0, though). Take a look at this [Erlang](https://github.com/silviucpp/ezstd/blob/f3f33b2f6b917f7e8aaa2b4d71338620537df81b/src/ezstd.erl#L151-L169) + [C++](https://github.com/silviucpp/ezstd/blob/f3f33b2f6b917f7e8aaa2b4d71338620537df81b/c_src/ezstd_nif.cc#L520-L568) implementation for inspiration.
## Tracking State
Most of a client's state is provided during the initial [Ready](/developers/events/gateway#ready-event) event and in the [Guild Create](/developers/events/gateway-events#guild-create) events that follow.
As resources continue to be created, updated, and deleted, Gateway events are sent to notify the app of these changes and to provide associated data. To avoid excessive API calls, apps should cache as many relevant resource states as possible, and update them as new events are received.
For larger apps, client state can grow to be very large. Therefore, we recommend only storing data in memory that are *needed* for the app to operate. In some cases, there isn't a need to cache member information (like roles or permissions) since some events like [MESSAGE\_CREATE](/developers/events/gateway-events#message-create) have the full member object included.
An example of state tracking can be considered in the case of an app that wants to track member status: when initially connecting to the Gateway, the app will receive information about the online status of guild members (whether they're online, idle, dnd, or offline). To keep the state updated, the app will track and parse [Presence Update](/developers/events/gateway-events#presence-update) events as they're received, then update the cached member objects accordingly.
## Guild Availability
When connecting to the gateway as a bot user, guilds that the bot is a part of will start out as unavailable. Don't fret! The gateway will automatically attempt to reconnect on your behalf. As guilds become available to you, you will receive [Guild Create](/developers/events/gateway-events#guild-create) events.
## Sharding
As apps grow and are added to an increasing number of guilds, some developers may find it necessary to divide portions of their app's operations across multiple processes. As such, the Gateway implements a method of user-controlled guild sharding which allows apps to split events across a number of Gateway connections. Guild sharding is entirely controlled by an app, and requires no state-sharing between separate connections to operate. While all apps *can* enable sharding, it's not necessary for apps in a smaller number of guilds.
Each shard can only support a maximum of 2500 guilds, and apps that are in 2500+ guilds *must* enable sharding.
To enable sharding on a connection, the app should send the `shard` array in the [Identify](/developers/events/gateway-events#identify) payload. The first item in this array should be the zero-based integer value of the current shard, while the second represents the total number of shards.
The [Get Gateway Bot](/developers/events/gateway#get-gateway-bot) endpoint provides a recommended number of shards for your app in the `shards` field
To calculate which events will be sent to which shard, the following formula can be used:
###### Sharding Formula
```python theme={"system"}
shard_id = (guild_id >> 22) % num_shards
```
As an example, if you wanted to split the connection between three shards, you'd use the following values for `shard` for each connection: `[0, 3]`, `[1, 3]`, and `[2, 3]`.
Gateway events that do not contain a `guild_id` will only be sent to the first shard (`shard_id: 0`). This includes Direct Message (DM), subscription and entitlement events.
Note that `num_shards` does not relate to (or limit) the total number of potential sessions. It is only used for *routing* traffic. As such, sessions do not have to be identified in an evenly-distributed manner when sharding. You can establish multiple sessions with the same `[shard_id, num_shards]`, or sessions with different `num_shards` values. This allows you to create sessions that will handle more or less traffic for more fine-tuned load balancing, or to orchestrate "zero-downtime" scaling/updating by handing off traffic to a new deployment of sessions with a higher or lower `num_shards` count that are prepared in parallel.
###### Max Concurrency
If you have multiple shards, you may start them concurrently based on the [`max_concurrency`](/developers/events/gateway#session-start-limit-object) value returned to you on session start. Which shards you can start concurrently are assigned based on a key for each shard. The rate limit key for a given shard can be computed with
```
rate_limit_key = shard_id % max_concurrency
```
This puts your shards into "buckets" of `max_concurrency` size. When you start your bot, you may start up to `max_concurrency` shards at a time, and you must start them by "bucket" **in order**. To explain another way, let's say you have 16 shards, and your `max_concurrency` is 16:
```
shard_id: 0, rate limit key (0 % 16): 0
shard_id: 1, rate limit key (1 % 16): 1
shard_id: 2, rate limit key (2 % 16): 2
shard_id: 3, rate limit key (3 % 16): 3
shard_id: 4, rate limit key (4 % 16): 4
shard_id: 5, rate limit key (5 % 16): 5
shard_id: 6, rate limit key (6 % 16): 6
shard_id: 7, rate limit key (7 % 16): 7
shard_id: 8, rate limit key (8 % 16): 8
shard_id: 9, rate limit key (9 % 16): 9
shard_id: 10, rate limit key (10 % 16): 10
shard_id: 11, rate limit key (11 % 16): 11
shard_id: 12, rate limit key (12 % 16): 12
shard_id: 13, rate limit key (13 % 16): 13
shard_id: 14, rate limit key (14 % 16): 14
shard_id: 15, rate limit key (15 % 16): 15
```
You may start all 16 of your shards at once, because each has a `rate_limit_key` which fills the bucket of 16 shards. However, let's say you had 32 shards:
```
shard_id: 0, rate limit key (0 % 16): 0
shard_id: 1, rate limit key (1 % 16): 1
shard_id: 2, rate limit key (2 % 16): 2
shard_id: 3, rate limit key (3 % 16): 3
shard_id: 4, rate limit key (4 % 16): 4
shard_id: 5, rate limit key (5 % 16): 5
shard_id: 6, rate limit key (6 % 16): 6
shard_id: 7, rate limit key (7 % 16): 7
shard_id: 8, rate limit key (8 % 16): 8
shard_id: 9, rate limit key (9 % 16): 9
shard_id: 10, rate limit key (10 % 16): 10
shard_id: 11, rate limit key (11 % 16): 11
shard_id: 12, rate limit key (12 % 16): 12
shard_id: 13, rate limit key (13 % 16): 13
shard_id: 14, rate limit key (14 % 16): 14
shard_id: 15, rate limit key (15 % 16): 15
shard_id: 16, rate limit key (16 % 16): 0
shard_id: 17, rate limit key (17 % 16): 1
shard_id: 18, rate limit key (18 % 16): 2
shard_id: 19, rate limit key (19 % 16): 3
shard_id: 20, rate limit key (20 % 16): 4
shard_id: 21, rate limit key (21 % 16): 5
shard_id: 22, rate limit key (22 % 16): 6
shard_id: 23, rate limit key (23 % 16): 7
shard_id: 24, rate limit key (24 % 16): 8
shard_id: 25, rate limit key (25 % 16): 9
shard_id: 26, rate limit key (26 % 16): 10
shard_id: 27, rate limit key (27 % 16): 11
shard_id: 28, rate limit key (28 % 16): 12
shard_id: 29, rate limit key (29 % 16): 13
shard_id: 30, rate limit key (30 % 16): 14
shard_id: 31, rate limit key (31 % 16): 15
```
In this case, you must start the shard buckets **in "order"**. That means that you can start shard 0 -> shard 15 concurrently, and then you can start shard 16 -> shard 31.
### Sharding for Large Bots
If your bot is in more than 150,000 guilds, there are some additional considerations you must take around sharding. Discord will migrate your bot to large bot sharding when it starts to get near the large bot sharding threshold. The bot owner(s) will receive a system DM and email confirming this move has completed as well as what shard number has been assigned.
The number of shards you run must be a multiple of the shard number provided when reaching out to you. If you attempt to start your bot with an invalid number of shards, your Gateway connection will close with a `4010` Invalid Shard close code.
The [Get Gateway Bot endpoint](/developers/events/gateway#get-gateway-bot) will always return the correct amount of shards, so if you're already using this endpoint to determine your number of shards, you shouldn't require any changes.
The session start limit for these bots will also be increased from 1000 to `max(2000, (guild_count / 1000) * 5)` per day. You also receive an increased `max_concurrency`, the number of [shards you can concurrently start](/developers/events/gateway#session-start-limit-object).
## Get Gateway
/gateway
This endpoint does not require authentication.
Returns an object with a valid WSS URL which the app can use when [Connecting](/developers/events/gateway#connecting) to the Gateway. Apps should cache this value and only call this endpoint to retrieve a new URL when they are unable to properly establish a connection using the cached one.
###### Example Response
```json theme={"system"}
{
"url": "wss://gateway.discord.gg/"
}
```
## Get Gateway Bot
/gateway/bot
This endpoint requires authentication using a valid bot token.
Returns an object based on the information in [Get Gateway](/developers/events/gateway#get-gateway), plus additional metadata that can help during the operation of large or [sharded](/developers/events/gateway#sharding) bots. Unlike the [Get Gateway](/developers/events/gateway#get-gateway), this route should not be cached for extended periods of time as the value is not guaranteed to be the same per-call, and changes as the bot joins/leaves guilds.
###### JSON Response
| Field | Type | Description |
| --------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| url | string | WSS URL that can be used for connecting to the Gateway |
| shards | integer | Recommended number of [shards](/developers/events/gateway#sharding) to use when connecting |
| session\_start\_limit | [session\_start\_limit](/developers/events/gateway#session-start-limit-object) object | Information on the current session start limit |
###### Example Response
```json theme={"system"}
{
"url": "wss://gateway.discord.gg/",
"shards": 9,
"session_start_limit": {
"total": 1000,
"remaining": 999,
"reset_after": 14400000,
"max_concurrency": 1
}
}
```
## Session Start Limit Object
###### Session Start Limit Structure
| Field | Type | Description |
| ---------------- | ------- | -------------------------------------------------------------- |
| total | integer | Total number of session starts the current user is allowed |
| remaining | integer | Remaining number of session starts the current user is allowed |
| reset\_after | integer | Number of milliseconds after which the limit resets |
| max\_concurrency | integer | Number of identify requests allowed per 5 seconds |
# Gateway Events
Source: https://docs.discord.com/developers/events/gateway-events
Complete reference for all Gateway events.
Gateway connections are WebSockets, meaning they're bidirectional and either side of the WebSocket can send events to the other. The following events are split up into two types:
* **Send events** are Gateway events sent by an app to Discord (like when identifying with the Gateway)
* **Receive events** are Gateway events that are sent by Discord to an app. These events typically represent something happening inside of a server where an app is installed, like a channel being updated.
All Gateway events are encapsulated in a [Gateway event payload](/developers/events/gateway-events#payload-structure).
For more information about interacting with the Gateway, you can reference the [Gateway documentation](/developers/events/gateway).
Not all Gateway event fields are documented. You should assume that undocumented fields are not supported for apps, and their format and data may change at any time.
### Event Names
In practice, event names are UPPER-CASED with under\_scores joining each word in the name. For instance, [Channel Create](/developers/events/gateway-events#channel-create) would be `CHANNEL_CREATE` and [Voice State Update](/developers/events/gateway-events#voice-state-update) would be `VOICE_STATE_UPDATE`.
For readability, event names in the following documentation are typically left in Title Case.
### Payload Structure
Gateway event payloads have a common structure, but the contents of the associated data (`d`) varies between the different events.
| Field | Type | Description |
| ----- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| op | integer | [Gateway opcode](/developers/topics/opcodes-and-status-codes#gateway-gateway-opcodes), which indicates the payload type |
| d | ?mixed (any JSON value) | Event data |
| s | ?integer \* | Sequence number of event used for [resuming sessions](/developers/events/gateway#resuming) and [heartbeating](/developers/events/gateway#sending-heartbeats) |
| t | ?string \* | Event name |
\* `s` and `t` are `null` when `op` is not `0` ([Gateway Dispatch opcode](/developers/topics/opcodes-and-status-codes#gateway-gateway-opcodes)).
###### Example Gateway Event Payload
```json theme={"system"}
{
"op": 0,
"d": {},
"s": 42,
"t": "GATEWAY_EVENT_NAME"
}
```
## Send Events
Send events are Gateway events encapsulated in an [event payload](/developers/events/gateway-events#payload-structure), and are sent by an app to Discord through a Gateway connection.
Previously, Gateway send events were labeled as commands
| Name | Description |
| ---------------------------------------------------------------------------------------- | --------------------------------------------------------- |
| [Identify](/developers/events/gateway-events#identify) | Triggers the initial handshake with the gateway |
| [Resume](/developers/events/gateway-events#resume) | Resumes a dropped gateway connection |
| [Heartbeat](/developers/events/gateway-events#heartbeat) | Maintains an active gateway connection |
| [Request Guild Members](/developers/events/gateway-events#request-guild-members) | Requests members for a guild |
| [Request Soundboard Sounds](/developers/events/gateway-events#request-soundboard-sounds) | Requests soundboard sounds in a set of guilds |
| [Request Channel Info](/developers/events/gateway-events#request-channel-info) | Requests ephemeral channel data for channels in a guild |
| [Update Voice State](/developers/events/gateway-events#update-voice-state) | Joins, moves, or disconnects the app from a voice channel |
| [Update Presence](/developers/events/gateway-events#update-presence) | Updates an app's presence |
#### Identify
Used to trigger the initial handshake with the gateway.
Details about identifying is in the [Gateway documentation](/developers/events/gateway#identifying).
###### Identify Structure
| Field | Type | Description | Default |
| ----------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | ------- |
| token | string | Authentication token | - |
| properties | object | [Connection properties](/developers/events/gateway-events#identify-identify-connection-properties) | - |
| compress? | boolean | Whether this connection supports compression of packets | false |
| large\_threshold? | integer | Value between 50 and 250, total number of members where the gateway will stop sending offline members in the guild member list | 50 |
| shard? | array of two integers (shard\_id, num\_shards) | Used for [Guild Sharding](/developers/events/gateway#sharding) | - |
| presence? | [update presence](/developers/events/gateway-events#update-presence) object | Presence structure for initial presence information | - |
| intents | integer | [Gateway Intents](/developers/events/gateway#gateway-intents) you wish to receive | - |
###### Identify Connection Properties
| Field | Type | Description |
| ------- | ------ | --------------------- |
| os | string | Your operating system |
| browser | string | Your library name |
| device | string | Your library name |
These fields originally were $ prefixed (i.e: `$browser\`) but [this syntax is deprecated](/developers/change-log#updated-connection-property-field-names). While they currently still work, it is recommended to move to non-prefixed fields.
###### Example Identify
```json theme={"system"}
{
"op": 2,
"d": {
"token": "my_token",
"properties": {
"os": "linux",
"browser": "disco",
"device": "disco"
},
"compress": true,
"large_threshold": 250,
"shard": [0, 1],
"presence": {
"activities": [{
"name": "Cards Against Humanity",
"type": 0
}],
"status": "dnd",
"since": 91879201,
"afk": false
},
// These intents represent 1 << 0 for GUILDS and 1 << 2 for GUILD_MODERATION
// This connection will only receive the events defined in those two intents
"intents": 5
}
}
```
#### Resume
Used to replay missed events when a disconnected client resumes.
Details about resuming are in the [Gateway documentation](/developers/events/gateway#resuming).
###### Resume Structure
| Field | Type | Description |
| ----------- | ------- | ----------------------------- |
| token | string | Session token |
| session\_id | string | Session ID |
| seq | integer | Last sequence number received |
###### Example Resume
```json theme={"system"}
{
"op": 6,
"d": {
"token": "randomstring",
"session_id": "evenmorerandomstring",
"seq": 1337
}
}
```
#### Heartbeat
Used to maintain an active gateway connection. Must be sent every `heartbeat_interval` milliseconds after the [Opcode 10 Hello](/developers/events/gateway-events#hello) payload is received. The inner `d` key is the last sequence number—`s`—received by the client. If you have not yet received one, send `null`.
Details about heartbeats are in the [Gateway documentation](/developers/events/gateway#sending-heartbeats).
###### Example Heartbeat
```json theme={"system"}
{
"op": 1,
"d": 251
}
```
#### Request Guild Members
Used to request all members for a guild or a list of guilds. When initially connecting, if you don't have the `GUILD_PRESENCES` [Gateway Intent](/developers/events/gateway#gateway-intents), or if the guild is over 75k members, it will only send members who are in voice, plus the member for you (the connecting user). Otherwise, if a guild has over `large_threshold` members (value in the [Gateway Identify](/developers/events/gateway-events#identify)), it will only send members who are online, have a role, have a nickname, or are in a voice channel, and if it has under `large_threshold` members, it will send all members. If a client wishes to receive additional members, they need to explicitly request them via this operation. The server will send [Guild Members Chunk](/developers/events/gateway-events#guild-members-chunk) events in response with up to 1000 members per chunk until all members that match the request have been sent.
Due to our privacy and infrastructural concerns with this feature, there are some limitations that apply:
* `GUILD_PRESENCES` intent is required to set `presences = true`. Otherwise, it will always be false
* `GUILD_MEMBERS` intent is required to request the entire member list—`(query=‘’, limit=0<=n)`
* You will be limited to requesting 1 `guild_id` per request
* Requesting a prefix (`query` parameter) will return a maximum of 100 members
* Requesting `user_ids` will continue to be limited to returning 100 members
We are introducing a new rate limit to the Request Guild Members opcode. See [Introducing Rate Limit When Requesting All Guild Members](/developers/change-log#introducing-rate-limit-when-requesting-all-guild-members) for more information and timeline on this new rate limit.
###### Request Guild Members Structure
| Field | Type | Description | Required |
| ---------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | -------------------------- |
| guild\_id | snowflake | ID of the guild to get members for | true |
| query? | string | string that username starts with, or an empty string to return all members | one of query or user\_ids |
| limit | integer | maximum number of members to send matching the `query`; a limit of `0` can be used with an empty string `query` to return all members | true when specifying query |
| presences? | boolean | used to specify if we want the presences of the matched members | false |
| user\_ids? | snowflake or array of snowflakes | used to specify which users you wish to fetch | one of query or user\_ids |
| nonce? | string | nonce to identify the [Guild Members Chunk](/developers/events/gateway-events#guild-members-chunk) response | false |
Nonce can only be up to 32 bytes. If you send an invalid nonce it will be ignored and the reply member\_chunk(s) will not have a nonce set.
###### Example Request Guild Members
```json theme={"system"}
{
"op": 8,
"d": {
"guild_id": "41771983444115456",
"query": "",
"limit": 0
}
}
```
#### Request Soundboard Sounds
Used to request soundboard sounds for a list of guilds. The server will send [Soundboard Sounds](/developers/events/gateway-events#soundboard-sounds) events for each guild in response.
###### Request Soundboard Sounds Structure
| Field | Type | Description |
| ---------- | ------------------- | ---------------------------------------------- |
| guild\_ids | array of snowflakes | IDs of the guilds to get soundboard sounds for |
###### Example Request Soundboard Sounds
```json theme={"system"}
{
"op": 31,
"d": {
"guild_ids": ["613425648685547541", "81384788765712384"]
}
}
```
### Request Channel Info
Requests ephemeral channel data for channels in a guild. The server will send a [Channel Info](/developers/events/gateway-events#channel-info) event in response.
###### Request Channel Info Structure
| Field | Type | Description |
| --------- | ---------------- | ---------------------------------------------------------------------------------------- |
| guild\_id | snowflake | The guild id to request channel info for |
| fields | array of strings | The fields to request. The current available fields are `status` and `voice_start_time`. |
###### Example Request Channel Info
```json theme={"system"}
{
"guild_id": "613425648685547541",
"fields": ["status", "voice_start_time"]
}
```
#### Update Voice State
Sent when a client wants to join, move, or disconnect from a voice channel.
###### Gateway Voice State Update Structure
| Field | Type | Description |
| ----------- | ---------- | -------------------------------------------------------------------- |
| guild\_id | snowflake | ID of the guild |
| channel\_id | ?snowflake | ID of the voice channel client wants to join (null if disconnecting) |
| self\_mute | boolean | Whether the client is muted |
| self\_deaf | boolean | Whether the client deafened |
###### Example Gateway Voice State Update
```json theme={"system"}
{
"op": 4,
"d": {
"guild_id": "41771983423143937",
"channel_id": "127121515262115840",
"self_mute": false,
"self_deaf": false
}
}
```
#### Update Presence
Sent by the client to indicate a presence or status update.
###### Gateway Presence Update Structure
| Field | Type | Description |
| ---------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------- |
| since | ?integer | Unix time (in milliseconds) of when the client went idle, or null if the client is not idle |
| activities | array of [activity](/developers/events/gateway-events#activity-object) objects | User's activities |
| status | string | User's new [status](/developers/events/gateway-events#update-presence-status-types) |
| afk | boolean | Whether or not the client is afk |
###### Status Types
| Status | Description |
| --------- | ------------------------------ |
| online | Online |
| dnd | Do Not Disturb |
| idle | AFK |
| invisible | Invisible and shown as offline |
| offline | Offline |
###### Example Gateway Presence Update
```json theme={"system"}
{
"op": 3,
"d": {
"since": 91879201,
"activities": [{
"name": "Save the Oxford Comma",
"type": 0
}],
"status": "online",
"afk": false
}
}
```
## Receive Events
Receive events are Gateway events encapsulated in an [event payload](/developers/events/gateway-events#payload-structure), and are sent by Discord to an app through a Gateway connection. Receive events correspond to events that happen in a Discord server where the app is installed.
| Name | Description |
| ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Hello](/developers/events/gateway-events#hello) | Defines the heartbeat interval |
| [Ready](/developers/events/gateway-events#ready) | Contains the initial state information |
| [Resumed](/developers/events/gateway-events#resumed) | Response to [Resume](/developers/events/gateway-events#resume) |
| [Reconnect](/developers/events/gateway-events#reconnect) | Server is going away, client should reconnect to gateway and resume |
| [Rate Limited](/developers/events/gateway-events#rate-limited) | Application was rate limited for a gateway opcode |
| [Invalid Session](/developers/events/gateway-events#invalid-session) | Failure response to [Identify](/developers/events/gateway-events#identify) or [Resume](/developers/events/gateway-events#resume) or invalid active session |
| [Application Command Permissions Update](/developers/events/gateway-events#application-command-permissions-update) | Application command permission was updated |
| [Auto Moderation Rule Create](/developers/events/gateway-events#auto-moderation-rule-create) | Auto Moderation rule was created |
| [Auto Moderation Rule Update](/developers/events/gateway-events#auto-moderation-rule-update) | Auto Moderation rule was updated |
| [Auto Moderation Rule Delete](/developers/events/gateway-events#auto-moderation-rule-delete) | Auto Moderation rule was deleted |
| [Auto Moderation Action Execution](/developers/events/gateway-events#auto-moderation-action-execution) | Auto Moderation rule was triggered and an action was executed (e.g. a message was blocked) |
| [Channel Create](/developers/events/gateway-events#channel-create) | New guild channel created |
| [Channel Update](/developers/events/gateway-events#channel-update) | Channel was updated |
| [Channel Delete](/developers/events/gateway-events#channel-delete) | Channel was deleted |
| [Channel Info](/developers/events/gateway-events#channel-info) | Response to [Request Channel Info](/developers/events/gateway-events#request-channel-info) |
| [Channel Pins Update](/developers/events/gateway-events#channel-pins-update) | Message was pinned or unpinned |
| [Thread Create](/developers/events/gateway-events#thread-create) | Thread created, also sent when being added to a private thread |
| [Thread Update](/developers/events/gateway-events#thread-update) | Thread was updated |
| [Thread Delete](/developers/events/gateway-events#thread-delete) | Thread was deleted |
| [Thread List Sync](/developers/events/gateway-events#thread-list-sync) | Sent when gaining access to a channel, contains all active threads in that channel |
| [Thread Member Update](/developers/events/gateway-events#thread-member-update) | [Thread member](/developers/resources/channel#thread-member-object) for the current user was updated |
| [Thread Members Update](/developers/events/gateway-events#thread-members-update) | Some user(s) were added to or removed from a thread |
| [Entitlement Create](/developers/events/gateway-events#entitlement-create) | Entitlement was created |
| [Entitlement Update](/developers/events/gateway-events#entitlement-update) | Entitlement was updated or renewed |
| [Entitlement Delete](/developers/events/gateway-events#entitlement-delete) | Entitlement was deleted |
| [Guild Create](/developers/events/gateway-events#guild-create) | Lazy-load for unavailable guild, guild became available, or user joined a new guild |
| [Guild Update](/developers/events/gateway-events#guild-update) | Guild was updated |
| [Guild Delete](/developers/events/gateway-events#guild-delete) | Guild became unavailable, or user left/was removed from a guild |
| [Guild Audit Log Entry Create](/developers/events/gateway-events#guild-audit-log-entry-create) | A guild audit log entry was created |
| [Guild Ban Add](/developers/events/gateway-events#guild-ban-add) | User was banned from a guild |
| [Guild Ban Remove](/developers/events/gateway-events#guild-ban-remove) | User was unbanned from a guild |
| [Guild Emojis Update](/developers/events/gateway-events#guild-emojis-update) | Guild emojis were updated |
| [Guild Stickers Update](/developers/events/gateway-events#guild-stickers-update) | Guild stickers were updated |
| [Guild Integrations Update](/developers/events/gateway-events#guild-integrations-update) | Guild integration was updated |
| [Guild Member Add](/developers/events/gateway-events#guild-member-add) | New user joined a guild |
| [Guild Member Remove](/developers/events/gateway-events#guild-member-remove) | User was removed from a guild |
| [Guild Member Update](/developers/events/gateway-events#guild-member-update) | Guild member was updated |
| [Guild Members Chunk](/developers/events/gateway-events#guild-members-chunk) | Response to [Request Guild Members](/developers/events/gateway-events#request-guild-members) |
| [Guild Role Create](/developers/events/gateway-events#guild-role-create) | Guild role was created |
| [Guild Role Update](/developers/events/gateway-events#guild-role-update) | Guild role was updated |
| [Guild Role Delete](/developers/events/gateway-events#guild-role-delete) | Guild role was deleted |
| [Guild Scheduled Event Create](/developers/events/gateway-events#guild-scheduled-event-create) | Guild scheduled event was created |
| [Guild Scheduled Event Update](/developers/events/gateway-events#guild-scheduled-event-update) | Guild scheduled event was updated |
| [Guild Scheduled Event Delete](/developers/events/gateway-events#guild-scheduled-event-delete) | Guild scheduled event was deleted |
| [Guild Scheduled Event User Add](/developers/events/gateway-events#guild-scheduled-event-user-add) | User subscribed to a guild scheduled event |
| [Guild Scheduled Event User Remove](/developers/events/gateway-events#guild-scheduled-event-user-remove) | User unsubscribed from a guild scheduled event |
| [Guild Soundboard Sound Create](/developers/events/gateway-events#guild-soundboard-sound-create) | Guild soundboard sound was created |
| [Guild Soundboard Sound Update](/developers/events/gateway-events#guild-soundboard-sound-update) | Guild soundboard sound was updated |
| [Guild Soundboard Sound Delete](/developers/events/gateway-events#guild-soundboard-sound-delete) | Guild soundboard sound was deleted |
| [Guild Soundboard Sounds Update](/developers/events/gateway-events#guild-soundboard-sounds-update) | Guild soundboard sounds were updated |
| [Soundboard Sounds](/developers/events/gateway-events#soundboard-sounds) | Response to [Request Soundboard Sounds](/developers/events/gateway-events#request-soundboard-sounds) |
| [Integration Create](/developers/events/gateway-events#integration-create) | Guild integration was created |
| [Integration Update](/developers/events/gateway-events#integration-update) | Guild integration was updated |
| [Integration Delete](/developers/events/gateway-events#integration-delete) | Guild integration was deleted |
| [Interaction Create](/developers/events/gateway-events#interaction-create) | User used an interaction, such as an [Application Command](/developers/interactions/application-commands) |
| [Invite Create](/developers/events/gateway-events#invite-create) | Invite to a channel was created |
| [Invite Delete](/developers/events/gateway-events#invite-delete) | Invite to a channel was deleted |
| [Message Create](/developers/events/gateway-events#message-create) | Message was created |
| [Message Update](/developers/events/gateway-events#message-update) | Message was edited |
| [Message Delete](/developers/events/gateway-events#message-delete) | Message was deleted |
| [Message Delete Bulk](/developers/events/gateway-events#message-delete-bulk) | Multiple messages were deleted at once |
| [Message Reaction Add](/developers/events/gateway-events#message-reaction-add) | User reacted to a message |
| [Message Reaction Remove](/developers/events/gateway-events#message-reaction-remove) | User removed a reaction from a message |
| [Message Reaction Remove All](/developers/events/gateway-events#message-reaction-remove-all) | All reactions were explicitly removed from a message |
| [Message Reaction Remove Emoji](/developers/events/gateway-events#message-reaction-remove-emoji) | All reactions for a given emoji were explicitly removed from a message |
| [Presence Update](/developers/events/gateway-events#presence-update) | User was updated |
| [Stage Instance Create](/developers/events/gateway-events#stage-instance-create) | Stage instance was created |
| [Stage Instance Update](/developers/events/gateway-events#stage-instance-update) | Stage instance was updated |
| [Stage Instance Delete](/developers/events/gateway-events#stage-instance-delete) | Stage instance was deleted or closed |
| [Subscription Create](/developers/events/gateway-events#subscription-create) | Premium App Subscription was created |
| [Subscription Update](/developers/events/gateway-events#subscription-update) | Premium App Subscription was updated |
| [Subscription Delete](/developers/events/gateway-events#subscription-delete) | Premium App Subscription was deleted |
| [Typing Start](/developers/events/gateway-events#typing-start) | User started typing in a channel |
| [User Update](/developers/events/gateway-events#user-update) | Properties about the user changed |
| [Voice Channel Effect Send](/developers/events/gateway-events#voice-channel-effect-send) | Someone sent an effect in a voice channel the current user is connected to |
| [Voice Channel Start Time Update](/developers/events/gateway-events#voice-channel-start-time-update) | Voice channel start time was updated |
| [Voice Channel Status Update](/developers/events/gateway-events#voice-channel-status-update) | Voice channel status was updated |
| [Voice State Update](/developers/events/gateway-events#voice-state-update) | Someone joined, left, or moved a voice channel |
| [Voice Server Update](/developers/events/gateway-events#voice-server-update) | Guild's voice server was updated |
| [Webhooks Update](/developers/events/gateway-events#webhooks-update) | Guild channel webhook was created, update, or deleted |
| [Message Poll Vote Add](/developers/events/gateway-events#message-poll-vote-add) | User voted on a poll |
| [Message Poll Vote Remove](/developers/events/gateway-events#message-poll-vote-remove) | User removed a vote on a poll |
#### Hello
Sent on connection to the websocket. Defines the heartbeat interval that an app should heartbeat to.
###### Hello Structure
| Field | Type | Description |
| ------------------- | ------- | ------------------------------------------------------- |
| heartbeat\_interval | integer | Interval (in milliseconds) an app should heartbeat with |
###### Example Hello
```json theme={"system"}
{
"op": 10,
"d": {
"heartbeat_interval": 45000
}
}
```
#### Ready
The ready event is dispatched when a client has completed the initial handshake with the gateway (for new sessions). The ready event can be the largest and most complex event the gateway will send, as it contains all the state required for a client to begin interacting with the rest of the platform.
`guilds` are the guilds of which your bot is a member. They start out as unavailable when you connect to the gateway. As they become available, your bot will be notified via [Guild Create](/developers/events/gateway-events#guild-create) events.
###### Ready Event Fields
| Field | Type | Description |
| -------------------- | ------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
| v | integer | [API version](/developers/reference#api-versioning-api-versions) |
| user | [user](/developers/resources/user#user-object) object | Information about the user including email |
| guilds | array of [Unavailable Guild](/developers/resources/guild#unavailable-guild-object) objects | Guilds the user is in |
| session\_id | string | Used for resuming connections |
| resume\_gateway\_url | string | Gateway URL for resuming connections |
| shard? | array of two integers (shard\_id, num\_shards) | [Shard information](/developers/events/gateway#sharding) associated with this session, if sent when identifying |
| application | partial [application object](/developers/resources/application#application-object) | Contains `id` and `flags` |
#### Resumed
The resumed event is dispatched when a client has sent a [resume payload](/developers/events/gateway-events#resume) to the gateway (for resuming existing sessions).
#### Reconnect
The reconnect event is dispatched when a client should reconnect to the gateway (and resume their existing session, if they have one). This can occur at any point in the gateway connection lifecycle, even before/in place of receiving a [Hello](/developers/events/gateway-events#hello) event. A few seconds after the reconnect event is dispatched, the connection may be closed by the server.
###### Example Gateway Reconnect
```json theme={"system"}
{
"op": 7,
"d": null
}
```
#### Invalid Session
Sent to indicate one of at least three different situations:
* the gateway could not initialize a session after receiving an [Opcode 2 Identify](/developers/events/gateway-events#identify)
* the gateway could not resume a previous session after receiving an [Opcode 6 Resume](/developers/events/gateway-events#resume)
* the gateway has invalidated an active session and is requesting client action
The inner `d` key is a boolean that indicates whether the session may be resumable. See [Connecting](/developers/events/gateway#connecting) and [Resuming](/developers/events/gateway#resuming) for more information.
###### Example Gateway Invalid Session
```json theme={"system"}
{
"op": 9,
"d": false
}
```
### Application Commands
#### Application Command Permissions Update
`APPLICATION_COMMAND_PERMISSIONS_UPDATE` event, sent when an application command's permissions are updated. The inner payload is an [application command permissions](/developers/interactions/application-commands#application-command-permissions-object-guild-application-command-permissions-structure) object.
### Auto Moderation
All [Auto Moderation](/developers/resources/auto-moderation) related events are only sent to bot users which have the `MANAGE_GUILD` permission.
#### Auto Moderation Rule Create
Sent when a rule is created. The inner payload is an [auto moderation rule](/developers/resources/auto-moderation#auto-moderation-rule-object) object.
#### Auto Moderation Rule Update
Sent when a rule is updated. The inner payload is an [auto moderation rule](/developers/resources/auto-moderation#auto-moderation-rule-object) object.
#### Auto Moderation Rule Delete
Sent when a rule is deleted. The inner payload is an [auto moderation rule](/developers/resources/auto-moderation#auto-moderation-rule-object) object.
#### Auto Moderation Action Execution
Sent when a rule is triggered and an action is executed (e.g. when a message is blocked).
###### Auto Moderation Action Execution Event Fields
| Field | Type | Description |
| --------------------------- | ---------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| guild\_id | snowflake | ID of the guild in which action was executed |
| action | [auto moderation action](/developers/resources/auto-moderation#auto-moderation-action-object) object | Action which was executed |
| rule\_id | snowflake | ID of the rule which action belongs to |
| rule\_trigger\_type | [trigger\_type](/developers/resources/auto-moderation#auto-moderation-rule-object-trigger-types) | Trigger type of rule which was triggered |
| user\_id | snowflake | ID of the user which generated the content which triggered the rule |
| channel\_id? | snowflake | ID of the channel in which user content was posted |
| message\_id? | snowflake | ID of any user message which content belongs to \* |
| alert\_system\_message\_id? | snowflake | ID of any system auto moderation messages posted as a result of this action \*\* |
| content \*\*\* | string | User-generated text content |
| matched\_keyword | ?string | Word or phrase configured in the rule that triggered the rule |
| matched\_content \*\*\* | ?string | Substring in content that triggered the rule |
\* `message_id` will not exist if message was blocked by [Auto Moderation](/developers/resources/auto-moderation) or content was not part of any message
\*\* `alert_system_message_id` will not exist if this event does not correspond to an action with type `SEND_ALERT_MESSAGE`
\*\*\* `MESSAGE_CONTENT` (`1 << 15`) [gateway intent](/developers/events/gateway#gateway-intents) is required to receive the `content` and `matched_content` fields
### Channels
#### Channel Create
Sent when a new guild channel is created, relevant to the current user. The inner payload is a [channel](/developers/resources/channel#channel-object) object.
#### Channel Update
Sent when a channel is updated. The inner payload is a [channel](/developers/resources/channel#channel-object) object. This is not sent when the field `last_message_id` is altered. To keep track of the last\_message\_id changes, you must listen for [Message Create](/developers/events/gateway-events#message-create) events (or [Thread Create](/developers/events/gateway-events#thread-create) events for `GUILD_FORUM` and `GUILD_MEDIA` channels).
This event may reference roles or guild members that no longer exist in the guild.
#### Channel Delete
Sent when a channel relevant to the current user is deleted. The inner payload is a [channel](/developers/resources/channel#channel-object) object.
#### Channel Info
Includes ephemeral data for channels in a guild. Sent in response to [Request Channel Info](/developers/events/gateway-events#request-channel-info).
###### Channel Info Structure
| Field | Type | Description |
| --------- | -------------------------------------------------------------------------------------------------------------- | ---------------------------------------- |
| guild\_id | snowflake | The guild id |
| channels | array of [channel info](/developers/events/gateway-events#channel-info-channel-info-channel-structure) objects | Ephemeral data for channels in the guild |
###### Channel Info Channel Structure
| Field | Type | Description |
| ------------------- | --------- | ------------------------------------------------------------- |
| id | snowflake | The channel id |
| status? | ?string | The voice channel status |
| voice\_start\_time? | ?integer | Unix timestamp (in seconds) of when the voice session started |
#### Voice Channel Status Update
Sent when the voice channel status changes.
| Field | Type | Description |
| --------- | --------- | ---------------------------- |
| id | snowflake | The channel id |
| guild\_id | snowflake | The guild id |
| status | ?string | The new voice channel status |
#### Voice Channel Start Time Update
Sent when the voice channel start time changes.
| Field | Type | Description |
| ------------------- | --------- | ------------------------------------------------------------- |
| id | snowflake | The channel id |
| guild\_id | snowflake | The guild id |
| voice\_start\_time? | ?integer | Unix timestamp (in seconds) of when the voice session started |
#### Thread Create
Sent when a thread is created, relevant to the current user, or when the current user is added to a thread. The inner payload is a [channel](/developers/resources/channel#channel-object) object.
* When a thread is created, includes an additional `newly_created` boolean field.
* When being added to an existing private thread, includes a [thread member](/developers/resources/channel#thread-member-object) object.
#### Thread Update
Sent when a thread is updated. The inner payload is a [channel](/developers/resources/channel#channel-object) object. This is not sent when the field `last_message_id` is altered. To keep track of the last\_message\_id changes, you must listen for [Message Create](/developers/events/gateway-events#message-create) events.
#### Thread Delete
Sent when a thread relevant to the current user is deleted. The inner payload is a subset of the [channel](/developers/resources/channel#channel-object) object, containing just the `id`, `guild_id`, `parent_id`, and `type` fields.
#### Thread List Sync
Sent when the current user *gains* access to a channel.
###### Thread List Sync Event Fields
| Field | Type | Description |
| ------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| guild\_id | snowflake | ID of the guild |
| channel\_ids? | array of snowflakes | Parent channel IDs whose threads are being synced. If omitted, then threads were synced for the entire guild. This array may contain channel\_ids that have no active threads as well, so you know to clear that data. |
| threads | array of [channel](/developers/resources/channel#channel-object) objects | All active threads in the given channels that the current user can access |
| members | array of [thread member](/developers/resources/channel#thread-member-object) objects | All thread member objects from the synced threads for the current user, indicating which threads the current user has been added to |
#### Thread Member Update
Sent when the [thread member](/developers/resources/channel#thread-member-object) object for the current user is updated. The inner payload is a [thread member](/developers/resources/channel#thread-member-object) object with an extra `guild_id` field. This event is documented for completeness, but unlikely to be used by most bots. For bots, this event largely is just a signal that you are a member of the thread. See the [threads docs](/developers/topics/threads) for more details.
###### Thread Member Update Event Extra Fields
| Field | Type | Description |
| --------- | --------- | --------------- |
| guild\_id | snowflake | ID of the guild |
#### Thread Members Update
Sent when anyone is added to or removed from a thread. If the current user does not have the `GUILD_MEMBERS` [Gateway Intent](/developers/events/gateway#gateway-intents), then this event will only be sent if the current user was added to or removed from the thread.
###### Thread Members Update Event Fields
| Field | Type | Description |
| --------------------- | ------------------------------------------------------------------------------------ | --------------------------------------------------------- |
| id | snowflake | ID of the thread |
| guild\_id | snowflake | ID of the guild |
| member\_count | integer | Approximate number of members in the thread, capped at 50 |
| added\_members?\* | array of [thread member](/developers/resources/channel#thread-member-object) objects | Users who were added to the thread |
| removed\_member\_ids? | array of snowflakes | ID of the users who were removed from the thread |
\* In this gateway event, the thread member objects will also include the [guild member](/developers/resources/guild#guild-member-object) and nullable [presence](/developers/events/gateway-events#presence) objects for each added thread member.
#### Channel Pins Update
Sent when a message is pinned or unpinned in a text channel. This is not sent when a pinned message is deleted.
###### Channel Pins Update Event Fields
| Field | Type | Description |
| --------------------- | ------------------ | ------------------------------------------------------- |
| guild\_id? | snowflake | ID of the guild |
| channel\_id | snowflake | ID of the channel |
| last\_pin\_timestamp? | ?ISO8601 timestamp | Time at which the most recent pinned message was pinned |
### Entitlements
#### Entitlement Create
Note: The `ENTITLEMENT_CREATE` event behavior changed on October 1, 2024. Please see the [Change Log and Entitlement Migration Guide](/developers/change-log#premium-apps-entitlement-migration-and-new-subscription-api) for more information on what changed.
Sent when an entitlement is created. The inner payload is an [entitlement](/developers/resources/entitlement#entitlement-object) object.
#### Entitlement Update
Note: The `ENTITLEMENT_UPDATE` event behavior changed on October 1, 2024. Please see the [Change Log and Entitlement Migration Guide](/developers/change-log#premium-apps-entitlement-migration-and-new-subscription-api) for more information on what changed.
Sent when an entitlement is updated. The inner payload is an [entitlement](/developers/resources/entitlement#entitlement-object) object.
For subscription entitlements, this event is triggered only when a user's subscription ends, providing an `ends_at` timestamp that indicates the end of the entitlement.
#### Entitlement Delete
Sent when an entitlement is deleted. The inner payload is an [entitlement](/developers/resources/entitlement#entitlement-object) object.
Entitlement deletions are infrequent, and occur when:
* Discord issues a refund for a subscription
* Discord removes an entitlement from a user via internal tooling
* Discord deletes an app-managed entitlement they created via the API
Entitlements are *not* deleted when they expire.
### Guilds
#### Guild Create
This event can be sent in three different scenarios:
1. When a user is initially connecting, to lazily load and backfill information for all unavailable guilds sent in the [Ready](/developers/events/gateway-events#ready) event. Guilds that are unavailable due to an outage will send a [Guild Delete](/developers/events/gateway-events#guild-delete) event.
2. When a Guild becomes available again to the client.
3. When the current user joins a new Guild.
During an outage, the guild object in scenarios 1 and 3 may be marked as unavailable.
The inner payload can be:
* An available Guild: a [guild](/developers/resources/guild#guild-object) object with extra fields, as noted below.
* An unavailable Guild: an [unavailable guild](/developers/resources/guild#unavailable-guild-object) object.
###### Guild Create Extra Fields
| Field | Type | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- |
| joined\_at | ISO8601 timestamp | When this guild was joined at |
| large | boolean | `true` if this is considered a large guild |
| unavailable? | boolean | `true` if this guild is unavailable due to an outage |
| member\_count | integer | Total number of members in this guild |
| voice\_states | array of partial [voice state](/developers/resources/voice#voice-state-object) objects | States of members currently in voice channels; lacks the `guild_id` key |
| members | array of [guild member](/developers/resources/guild#guild-member-object) objects | Users in the guild |
| channels | array of [channel](/developers/resources/channel#channel-object) objects | Channels in the guild |
| threads | array of [channel](/developers/resources/channel#channel-object) objects | All active threads in the guild that current user has permission to view |
| presences | array of partial [presence update](/developers/events/gateway-events#presence-update) objects | Presences of the members in the guild, will only include non-offline members if the size is greater than `large threshold` |
| stage\_instances | array of [stage instance](/developers/resources/stage-instance#stage-instance-object) objects | Stage instances in the guild |
| guild\_scheduled\_events | array of [guild scheduled event](/developers/resources/guild-scheduled-event#guild-scheduled-event-object) objects | Scheduled events in the guild |
| soundboard\_sounds | array of [soundboard sound](/developers/resources/soundboard#soundboard-sound-object) objects | Soundboard sounds in the guild |
If your bot does not have the `GUILD_PRESENCES` [Gateway Intent](/developers/events/gateway#gateway-intents), or if the guild has over 75k members, members and presences returned in this event will only contain your bot and users in voice channels.
#### Guild Update
Sent when a guild is updated. The inner payload is a [guild](/developers/resources/guild#guild-object) object.
#### Guild Delete
Sent when a guild becomes or was already unavailable due to an outage, or when the user leaves or is removed from a guild. The inner payload is an [unavailable guild](/developers/resources/guild#unavailable-guild-object) object. If the `unavailable` field is not set, the user was removed from the guild.
#### Guild Audit Log Entry Create
Sent when a guild audit log entry is created. The inner payload is an [Audit Log Entry](/developers/resources/audit-log#audit-log-entry-object) object with an extra `guild_id` key. This event is only sent to bots with the `VIEW_AUDIT_LOG` permission.
###### Guild Audit Log Entry Create Event Extra Fields
| Field | Type | Description |
| --------- | --------- | --------------- |
| guild\_id | snowflake | ID of the guild |
#### Guild Ban Add
Sent when a user is banned from a guild. This event is only sent to bots with the `BAN_MEMBERS` or `VIEW_AUDIT_LOG` permission.
###### Guild Ban Add Event Fields
| Field | Type | Description |
| --------- | ------------------------------------------------------- | ------------------- |
| guild\_id | snowflake | ID of the guild |
| user | a [user](/developers/resources/user#user-object) object | User who was banned |
#### Guild Ban Remove
Sent when a user is unbanned from a guild. This event is only sent to bots with the `BAN_MEMBERS` or `VIEW_AUDIT_LOG` permission.
###### Guild Ban Remove Event Fields
| Field | Type | Description |
| --------- | ------------------------------------------------------- | --------------------- |
| guild\_id | snowflake | ID of the guild |
| user | a [user](/developers/resources/user#user-object) object | User who was unbanned |
#### Guild Emojis Update
Sent when a guild's emojis have been updated.
###### Guild Emojis Update Event Fields
| Field | Type | Description |
| --------- | --------- | ----------------------------------------------------------- |
| guild\_id | snowflake | ID of the guild |
| emojis | array | Array of [emojis](/developers/resources/emoji#emoji-object) |
#### Guild Stickers Update
Sent when a guild's stickers have been updated.
###### Guild Stickers Update Event Fields
| Field | Type | Description |
| --------- | --------- | ----------------------------------------------------------------- |
| guild\_id | snowflake | ID of the guild |
| stickers | array | Array of [stickers](/developers/resources/sticker#sticker-object) |
#### Guild Integrations Update
Sent when a guild integration is updated.
###### Guild Integrations Update Event Fields
| Field | Type | Description |
| --------- | --------- | ----------------------------------------------- |
| guild\_id | snowflake | ID of the guild whose integrations were updated |
#### Guild Member Add
If using [Gateway Intents](/developers/events/gateway#gateway-intents), the `GUILD_MEMBERS` intent will be required to receive this event.
Sent when a user joins a guild. This event may also be sent for users who are already members of the guild. The inner payload is a [guild member](/developers/resources/guild#guild-member-object) object with an extra `guild_id` key:
###### Guild Member Add Extra Fields
| Field | Type | Description |
| --------- | --------- | --------------- |
| guild\_id | snowflake | ID of the guild |
#### Guild Member Remove
If using [Gateway Intents](/developers/events/gateway#gateway-intents), the `GUILD_MEMBERS` intent will be required to receive this event.
Sent when a user is removed from a guild (leave/kick/ban).
###### Guild Member Remove Event Fields
| Field | Type | Description |
| --------- | ------------------------------------------------------- | -------------------- |
| guild\_id | snowflake | ID of the guild |
| user | a [user](/developers/resources/user#user-object) object | User who was removed |
#### Guild Member Update
If using [Gateway Intents](/developers/events/gateway#gateway-intents), the `GUILD_MEMBERS` intent will be required to receive this event.
Sent when a guild member is updated. This will also fire when the user object of a guild member changes.
###### Guild Member Update Event Fields
| Field | Type | Description |
| ------------------------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| guild\_id | snowflake | ID of the guild |
| roles | array of snowflakes | User role ids |
| user | a [user](/developers/resources/user#user-object) object | User |
| nick? | ?string | Nickname of the user in the guild |
| avatar | ?string | Member's [guild avatar hash](/developers/reference#image-formatting) |
| banner | ?string | Member's [guild banner hash](/developers/reference#image-formatting) |
| joined\_at | ?ISO8601 timestamp | When the user joined the guild |
| premium\_since? | ?ISO8601 timestamp | When the user starting [boosting](https://support.discord.com/hc/en-us/articles/360028038352-Server-Boosting-) the guild |
| deaf? | boolean | Whether the user is deafened in voice channels |
| mute? | boolean | Whether the user is muted in voice channels |
| pending? | boolean | Whether the user has not yet passed the guild's [Membership Screening](/developers/resources/guild#membership-screening-object) requirements |
| communication\_disabled\_until? | ?ISO8601 timestamp | When the user's [timeout](https://support.discord.com/hc/en-us/articles/4413305239191-Time-Out-FAQ) will expire and the user will be able to communicate in the guild again, null or a time in the past if the user is not timed out |
| avatar\_decoration\_data? | ?[avatar decoration data](/developers/resources/user#avatar-decoration-data-object) | Data for the member's guild avatar decoration |
| collectibles? | ?[collectibles](/developers/resources/user#collectibles) object | data for the member's collectibles |
#### Guild Members Chunk
Sent in response to [Guild Request Members](/developers/events/gateway-events#request-guild-members).
You can use the `chunk_index` and `chunk_count` to calculate how many chunks are left for your request.
###### Guild Members Chunk Event Fields
| Field | Type | Description |
| ------------ | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| guild\_id | snowflake | ID of the guild |
| members | array of [guild member](/developers/resources/guild#guild-member-object) objects | Set of guild members |
| chunk\_index | integer | Chunk index in the expected chunks for this response (`0 <= chunk_index < chunk_count`) |
| chunk\_count | integer | Total number of expected chunks for this response |
| not\_found? | array | When passing an invalid ID to `REQUEST_GUILD_MEMBERS`, it will be returned here |
| presences? | array of [presence](/developers/events/gateway-events#presence) objects | When passing `true` to `REQUEST_GUILD_MEMBERS`, presences of the returned members will be here |
| nonce? | string | Nonce used in the [Guild Members Request](/developers/events/gateway-events#request-guild-members) |
#### Guild Role Create
Sent when a guild role is created.
###### Guild Role Create Event Fields
| Field | Type | Description |
| --------- | ----------------------------------------------------------- | --------------------- |
| guild\_id | snowflake | ID of the guild |
| role | a [role](/developers/topics/permissions#role-object) object | Role that was created |
#### Guild Role Update
Sent when a guild role is updated.
###### Guild Role Update Event Fields
| Field | Type | Description |
| --------- | ----------------------------------------------------------- | --------------------- |
| guild\_id | snowflake | ID of the guild |
| role | a [role](/developers/topics/permissions#role-object) object | Role that was updated |
#### Guild Role Delete
Sent when a guild role is deleted.
###### Guild Role Delete Event Fields
| Field | Type | Description |
| --------- | --------- | --------------- |
| guild\_id | snowflake | ID of the guild |
| role\_id | snowflake | ID of the role |
#### Guild Scheduled Event Create
Sent when a guild scheduled event is created. The inner payload is a [guild scheduled event](/developers/resources/guild-scheduled-event#guild-scheduled-event-object) object.
#### Guild Scheduled Event Update
Sent when a guild scheduled event is updated. The inner payload is a [guild scheduled event](/developers/resources/guild-scheduled-event#guild-scheduled-event-object) object.
#### Guild Scheduled Event Delete
Sent when a guild scheduled event is deleted. The inner payload is a [guild scheduled event](/developers/resources/guild-scheduled-event#guild-scheduled-event-object) object.
#### Guild Scheduled Event User Add
Sent when a user has subscribed to a guild scheduled event.
###### Guild Scheduled Event User Add Event Fields
| Field | Type | Description |
| --------------------------- | --------- | ------------------------------- |
| guild\_scheduled\_event\_id | snowflake | ID of the guild scheduled event |
| user\_id | snowflake | ID of the user |
| guild\_id | snowflake | ID of the guild |
#### Guild Scheduled Event User Remove
Sent when a user has unsubscribed from a guild scheduled event.
###### Guild Scheduled Event User Remove Event Fields
| Field | Type | Description |
| --------------------------- | --------- | ------------------------------- |
| guild\_scheduled\_event\_id | snowflake | ID of the guild scheduled event |
| user\_id | snowflake | ID of the user |
| guild\_id | snowflake | ID of the guild |
#### Guild Soundboard Sound Create
Sent when a guild soundboard sound is created. The inner payload is a [soundboard sound](/developers/resources/soundboard#soundboard-sound-object) object.
#### Guild Soundboard Sound Update
Sent when a guild soundboard sound is updated. The inner payload is a [soundboard sound](/developers/resources/soundboard#soundboard-sound-object) object.
#### Guild Soundboard Sound Delete
Sent when a guild soundboard sound is deleted.
###### Guild Soundboard Sound Delete Event Fields
| Field | Type | Description |
| --------- | --------- | -------------------------------- |
| sound\_id | snowflake | ID of the sound that was deleted |
| guild\_id | snowflake | ID of the guild the sound was in |
#### Guild Soundboard Sounds Update
Sent when multiple guild soundboard sounds are updated.
###### Guild Soundboard Sounds Update Event Fields
| Field | Type | Description |
| ------------------ | --------------------------------------------------------------------------------------------- | ----------------------------- |
| soundboard\_sounds | array of [soundboard sound](/developers/resources/soundboard#soundboard-sound-object) objects | The guild's soundboard sounds |
| guild\_id | snowflake | ID of the guild |
#### Soundboard Sounds
Includes a guild's list of soundboard sounds. Sent in response to [Request Soundboard Sounds](/developers/events/gateway-events#request-soundboard-sounds).
###### Soundboard Sounds Event Fields
| Field | Type | Description |
| ------------------ | --------------------------------------------------------------------------------------------- | ----------------------------- |
| soundboard\_sounds | array of [soundboard sound](/developers/resources/soundboard#soundboard-sound-object) objects | The guild's soundboard sounds |
| guild\_id | snowflake | ID of the guild |
### Integrations
#### Integration Create
Sent when an integration is created. The inner payload is an [integration](/developers/resources/guild#integration-object) object with `user` omitted and an additional `guild_id` key:
###### Integration Create Event Additional Fields
| Field | Type | Description |
| --------- | --------- | --------------- |
| guild\_id | snowflake | ID of the guild |
#### Integration Update
Sent when an integration is updated. The inner payload is an [integration](/developers/resources/guild#integration-object) object with `user` omitted and an additional `guild_id` key:
###### Integration Update Event Additional Fields
| Field | Type | Description |
| --------- | --------- | --------------- |
| guild\_id | snowflake | ID of the guild |
#### Integration Delete
Sent when an integration is deleted.
###### Integration Delete Event Fields
| Field | Type | Description |
| ---------------- | --------- | ------------------------------------------------------------- |
| id | snowflake | Integration ID |
| guild\_id | snowflake | ID of the guild |
| application\_id? | snowflake | ID of the bot/OAuth2 application for this discord integration |
### Invites
All [Invite](/developers/resources/invite) related events are only sent to bot users with the `MANAGE_CHANNELS` permission on the channel.
#### Invite Create
Sent when a new invite to a channel is created.
###### Invite Create Event Fields
| Field | Type | Description |
| -------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| channel\_id | snowflake | Channel the invite is for |
| code | string | Unique invite [code](/developers/resources/invite#invite-object) |
| created\_at | ISO8601 timestamp | Time at which the invite was created |
| guild\_id? | snowflake | Guild of the invite |
| inviter? | [user](/developers/resources/user#user-object) object | User that created the invite |
| max\_age | integer | How long the invite is valid for (in seconds) |
| max\_uses | integer | Maximum number of times the invite can be used |
| target\_type? | integer | [Type of target](/developers/resources/invite#invite-object-invite-target-types) for this voice channel invite |
| target\_user? | [user](/developers/resources/user#user-object) object | User whose stream to display for this voice channel stream invite |
| target\_application? | partial [application](/developers/resources/application#application-object) object | Embedded application to open for this voice channel embedded application invite |
| temporary | boolean | Whether or not the invite is temporary (invited users will be kicked on disconnect unless they're assigned a role) |
| uses | integer | How many times the invite has been used (always will be 0) |
| expires\_at | ?ISO8601 timestamp | the expiration date of this invite |
| role\_ids? | array of snowflakes | the role ID(s) for [roles](/developers/topics/permissions#role-object) in the guild given to the users that accept this invite |
#### Invite Delete
Sent when an invite is deleted.
###### Invite Delete Event Fields
| Field | Type | Description |
| ----------- | --------- | ---------------------------------------------------------------- |
| channel\_id | snowflake | Channel of the invite |
| guild\_id? | snowflake | Guild of the invite |
| code | string | Unique invite [code](/developers/resources/invite#invite-object) |
### Messages
Unlike persistent messages, ephemeral messages are sent directly to the user and the bot who sent the message rather than through the guild channel. Because of this, ephemeral messages are tied to the [`DIRECT_MESSAGES` intent](/developers/events/gateway#list-of-intents), and the message object won't include `guild_id` or `member`.
#### Message Create
Sent when a message is created. The inner payload is a [message](/developers/resources/message#message-object) object with the following extra fields:
###### Message Create Extra Fields
| Field | Type | Description |
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| guild\_id? | snowflake | ID of the guild the message was sent in - unless it is an ephemeral message |
| member? | partial [guild member](/developers/resources/guild#guild-member-object) object | Member properties for this message's author. Missing for ephemeral messages and messages from webhooks |
| mentions | array of [user](/developers/resources/user#user-object) objects, optionally with an additional partial [member](/developers/resources/guild#guild-member-object) field | Users specifically mentioned in the message |
| channel\_type? | integer | The [type of channel](/developers/resources/channel#channel-object-channel-types) the message was sent in |
#### Message Update
Sent when a message is updated. The inner payload is a [message](/developers/resources/message#message-object) object with the same extra fields as [MESSAGE\_CREATE](/developers/events/gateway-events#message-create).
The value for `tts` will always be false in message updates.
#### Message Delete
Sent when a message is deleted.
###### Message Delete Event Fields
| Field | Type | Description |
| ----------- | --------- | ----------------- |
| id | snowflake | ID of the message |
| channel\_id | snowflake | ID of the channel |
| guild\_id? | snowflake | ID of the guild |
#### Message Delete Bulk
Sent when multiple messages are deleted at once.
###### Message Delete Bulk Event Fields
| Field | Type | Description |
| ----------- | ------------------- | ------------------- |
| ids | array of snowflakes | IDs of the messages |
| channel\_id | snowflake | ID of the channel |
| guild\_id? | snowflake | ID of the guild |
#### Message Reaction Add
Sent when a user adds a reaction to a message.
###### Message Reaction Add Event Fields
| Field | Type | Description |
| -------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------ |
| user\_id | snowflake | ID of the user |
| channel\_id | snowflake | ID of the channel |
| message\_id | snowflake | ID of the message |
| guild\_id? | snowflake | ID of the guild |
| member? | [member](/developers/resources/guild#guild-member-object) object | Member who reacted if this happened in a guild |
| emoji | a partial [emoji](/developers/resources/emoji#emoji-object) object | Emoji used to react - [example](/developers/resources/emoji#emoji-object-standard-emoji-example) |
| message\_author\_id? | snowflake | ID of the user who authored the message which was reacted to |
| burst | boolean | true if this is a super-reaction |
| burst\_colors? | array of strings | Colors used for super-reaction animation in "#rrggbb" format |
| type | integer | The [type of reaction](/developers/resources/message#get-reactions-reaction-types) |
#### Message Reaction Remove
Sent when a user removes a reaction from a message.
###### Message Reaction Remove Event Fields
| Field | Type | Description |
| ----------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------ |
| user\_id | snowflake | ID of the user |
| channel\_id | snowflake | ID of the channel |
| message\_id | snowflake | ID of the message |
| guild\_id? | snowflake | ID of the guild |
| emoji | a partial [emoji](/developers/resources/emoji#emoji-object) object | Emoji used to react - [example](/developers/resources/emoji#emoji-object-standard-emoji-example) |
| burst | boolean | true if this was a super-reaction |
| type | integer | The [type of reaction](/developers/resources/message#get-reactions-reaction-types) |
#### Message Reaction Remove All
Sent when a user explicitly removes all reactions from a message.
###### Message Reaction Remove All Event Fields
| Field | Type | Description |
| ----------- | --------- | ----------------- |
| channel\_id | snowflake | ID of the channel |
| message\_id | snowflake | ID of the message |
| guild\_id? | snowflake | ID of the guild |
#### Message Reaction Remove Emoji
Sent when a bot removes all instances of a given emoji from the reactions of a message.
###### Message Reaction Remove Emoji Event Fields
| Field | Type | Description |
| ----------- | ---------------------------------------------------------------- | ---------------------- |
| channel\_id | snowflake | ID of the channel |
| guild\_id? | snowflake | ID of the guild |
| message\_id | snowflake | ID of the message |
| emoji | [partial emoji object](/developers/resources/emoji#emoji-object) | Emoji that was removed |
### Presence
#### Presence Update
If you are using [Gateway Intents](/developers/events/gateway#gateway-intents), you *must* specify the `GUILD_PRESENCES` intent in order to receive Presence Update events
A user's presence is their current state on a guild. This event is sent when a user's presence or info, such as name or avatar, is updated.
The user object within this event can be partial, the only field which must be sent is the `id` field, everything else is optional. Along with this limitation, no fields are required, and the types of the fields are **not** validated. Your client should expect any combination of fields and types within this event.
###### Presence Update Event Fields
| Field | Type | Description |
| -------------- | ------------------------------------------------------------------------------- | -------------------------------------------- |
| user | [user](/developers/resources/user#user-object) object | User whose presence is being updated |
| guild\_id | snowflake | ID of the guild |
| status | string | Either "idle", "dnd", "online", or "offline" |
| activities | array of [activity](/developers/events/gateway-events#activity-object) objects | User's current activities |
| client\_status | [client\_status](/developers/events/gateway-events#client-status-object) object | User's platform-dependent status |
#### Client Status Object
Active sessions are indicated with an "online", "idle", or "dnd" string per platform. If a user is offline or invisible, the corresponding field is not present.
| Field | Type | Description |
| -------- | ------ | --------------------------------------------------------------------------------- |
| desktop? | string | User's status set for an active desktop (Windows, Linux, Mac) application session |
| mobile? | string | User's status set for an active mobile (iOS, Android) application session |
| web? | string | User's status set for an active web (browser, bot user) application session |
#### Activity Object
###### Activity Structure
| Field | Type | Description |
| ---------------------- | ------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name | string | Activity's name |
| type | integer | [Activity type](/developers/events/gateway-events#activity-object-activity-types) |
| url? | ?string | Stream URL, is validated when type is 1 |
| created\_at | integer | Unix timestamp (in milliseconds) of when the activity was added to the user's session |
| timestamps? | [timestamps](/developers/events/gateway-events#activity-object-activity-timestamps) object | Unix timestamps for start and/or end of the game |
| application\_id? | snowflake | Application ID for the game |
| status\_display\_type? | ?integer | [Status display type](/developers/events/gateway-events#activity-object-status-display-types); controls which field is displayed in the user's status text in the member list |
| details? | ?string | What the player is currently doing |
| details\_url? | ?string | URL that is linked when clicking on the details text |
| state? | ?string | User's current party status, or text used for a custom status |
| state\_url? | ?string | URL that is linked when clicking on the state text |
| emoji? | ?[emoji](/developers/events/gateway-events#activity-object-activity-emoji) object | Emoji used for a custom status |
| party? | [party](/developers/events/gateway-events#activity-object-activity-party) object | Information for the current party of the player |
| assets? | [assets](/developers/events/gateway-events#activity-object-activity-assets) object | Images for the presence and their hover texts |
| secrets? | [secrets](/developers/events/gateway-events#activity-object-activity-secrets) object | Secrets for Rich Presence joining and spectating |
| instance? | boolean | Whether or not the activity is an instanced game session |
| flags? | integer | [Activity flags](/developers/events/gateway-events#activity-object-activity-flags) `OR`d together, describes what the payload includes |
| buttons? | array of [buttons](/developers/events/gateway-events#activity-object-activity-buttons) | Custom buttons shown in the Rich Presence (max 2) |
Bot users are only able to set `name`, `state`, `type`, and `url`.
###### Activity Types
| ID | Name | Format | Example |
| -- | --------- | --------------------- | ------------------------------------ |
| 0 | Playing | Playing `{name}` | "Playing Rocket League" |
| 1 | Streaming | Streaming `{details}` | "Streaming Rocket League" |
| 2 | Listening | Listening to `{name}` | "Listening to Spotify" |
| 3 | Watching | Watching `{name}` | "Watching YouTube Together" |
| 4 | Custom | `{emoji}` `{state}` | ":smiley: I am cool" |
| 5 | Competing | Competing in `{name}` | "Competing in Arena World Champions" |
The streaming type currently only supports Twitch and YouTube. Only `https://twitch.tv/` and `https://youtube.com/` urls will work.
###### Status Display Types
| ID | Name | Example |
| -- | ------- | -------------------------------------- |
| 0 | Name | "Listening to Spotify" |
| 1 | State | "Listening to Rick Astley" |
| 2 | Details | "Listening to Never Gonna Give You Up" |
This applies to all activity types. "Listening" was used to serve as a consistent example of what the different fields might be used for.
###### Activity Timestamps
| Field | Type | Description |
| ------ | ------- | -------------------------------------------------------- |
| start? | integer | Unix time (in milliseconds) of when the activity started |
| end? | integer | Unix time (in milliseconds) of when the activity ends |
For Listening and Watching activities, you can include both start and end timestamps to display a time bar.
###### Activity Emoji
| Field | Type | Description |
| --------- | --------- | ----------------------------- |
| name | string | Name of the emoji |
| id? | snowflake | ID of the emoji |
| animated? | boolean | Whether the emoji is animated |
###### Activity Party
| Field | Type | Description |
| ----- | ------------------------------------------------ | ------------------------------------------------- |
| id? | string | ID of the party |
| size? | array of two integers (current\_size, max\_size) | Used to show the party's current and maximum size |
###### Activity Assets
| Field | Type | Description |
| --------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| large\_image? | string | See [Activity Asset Image](/developers/events/gateway-events#activity-object-activity-asset-image) |
| large\_text? | string | Text displayed when hovering over the large image of the activity |
| large\_url? | string | URL that is opened when clicking on the large image |
| small\_image? | string | See [Activity Asset Image](/developers/events/gateway-events#activity-object-activity-asset-image) |
| small\_text? | string | Text displayed when hovering over the small image of the activity |
| small\_url? | string | URL that is opened when clicking on the small image |
| invite\_cover\_image? | string | See [Activity Asset Image](/developers/events/gateway-events#activity-object-activity-asset-image). Displayed as a banner on a [Game Invite](/developers/discord-social-sdk/development-guides/managing-game-invites). |
###### Activity Asset Image
Activity asset images are arbitrary strings which usually contain snowflake IDs or prefixed image IDs. Treat data within this field carefully, as it is user-specifiable and not sanitized.
To use an external image via media proxy, specify the URL as the field's value when sending. You will only receive the `mp:` prefix via the gateway.
| Type | Format | Image URL |
| ----------------- | ------------------------ | -------------------------------------------------------------------------------- |
| Application Asset | `{application_asset_id}` | See [Application Asset Image Formatting](/developers/reference#image-formatting) |
| Media Proxy Image | `mp:{image_id}` | `https://media.discordapp.net/{image_id}` |
**Uploaded assets** (Application Asset type) support PNG, JPEG, and WebP. Animated images are not supported for uploaded assets.
**External URL assets** (Media Proxy Image type) support any publicly accessible image URL, including GIF, animated WebP, and AVIF.
###### Activity Secrets
| Field | Type | Description |
| --------- | ------ | ------------------------------------- |
| join? | string | Secret for joining a party |
| spectate? | string | Secret for spectating a game |
| match? | string | Secret for a specific instanced match |
###### Activity Flags
| Name | Value |
| ------------------------------ | -------- |
| INSTANCE | `1 << 0` |
| JOIN | `1 << 1` |
| SPECTATE | `1 << 2` |
| JOIN\_REQUEST | `1 << 3` |
| SYNC | `1 << 4` |
| PLAY | `1 << 5` |
| PARTY\_PRIVACY\_FRIENDS | `1 << 6` |
| PARTY\_PRIVACY\_VOICE\_CHANNEL | `1 << 7` |
| EMBEDDED | `1 << 8` |
###### Activity Buttons
When received over the gateway, the `buttons` field is an array of strings, which are the button labels. Bots cannot access a user's activity button URLs. When sending, the `buttons` field must be an array of the below object:
| Field | Type | Description |
| ----- | ------ | ------------------------------------------------------ |
| label | string | Text shown on the button (1-32 characters) |
| url | string | URL opened when clicking the button (1-512 characters) |
###### Example Activity
```json theme={"system"}
{
"details": "24H RL Stream for Charity",
"state": "Rocket League",
"name": "Twitch",
"type": 1,
"url": "https://www.twitch.tv/discord"
}
```
###### Example Activity with Rich Presence
```json theme={"system"}
{
"name": "Rocket League",
"type": 0,
"application_id": "379286085710381999",
"state": "In a Match",
"details": "Ranked Duos: 2-1",
"timestamps": {
"start": 15112000660000
},
"party": {
"id": "9dd6594e-81b3-49f6-a6b5-a679e6a060d3",
"size": [2, 2]
},
"assets": {
"large_image": "351371005538729000",
"large_text": "DFH Stadium",
"small_image": "351371005538729111",
"small_text": "Silver III"
},
"secrets": {
"join": "025ed05c71f639de8bfaa0d679d7c94b2fdce12f",
"spectate": "e7eb30d2ee025ed05c71ea495f770b76454ee4e0",
"match": "4b2fdce12f639de8bfa7e3591b71a0d679d7c93f"
}
}
```
Clients may only update their game status 5 times per 20 seconds.
#### Typing Start
Sent when a user starts typing in a channel.
###### Typing Start Event Fields
| Field | Type | Description |
| ----------- | ---------------------------------------------------------------- | ------------------------------------------------------ |
| channel\_id | snowflake | ID of the channel |
| guild\_id? | snowflake | ID of the guild |
| user\_id | snowflake | ID of the user |
| timestamp | integer | Unix time (in seconds) of when the user started typing |
| member? | [member](/developers/resources/guild#guild-member-object) object | Member who started typing if this happened in a guild |
#### User Update
Sent when properties about the current bot's user change. Inner payload is a [user](/developers/resources/user#user-object) object.
### Voice
#### Voice Channel Effect Send
Sent when someone sends an effect, such as an emoji reaction or a soundboard sound, in a voice channel the current user is connected to.
###### Voice Channel Effect Send Event Fields
| Field | Type | Description |
| ---------------- | --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| channel\_id | snowflake | ID of the channel the effect was sent in |
| guild\_id | snowflake | ID of the guild the effect was sent in |
| user\_id | snowflake | ID of the user who sent the effect |
| emoji? | ?[emoji](/developers/resources/emoji#emoji-object) object | The emoji sent, for emoji reaction and soundboard effects |
| animation\_type? | ?integer | The [type of emoji animation](/developers/events/gateway-events#voice-channel-effect-send-animation-types), for emoji reaction and soundboard effects |
| animation\_id? | integer | The ID of the emoji animation, for emoji reaction and soundboard effects |
| sound\_id? | snowflake or integer | The ID of the soundboard sound, for soundboard effects |
| sound\_volume? | double | The volume of the soundboard sound, from 0 to 1, for soundboard effects |
###### Animation Types
| Type | Value | Description |
| ------- | ----- | ------------------------------------------- |
| PREMIUM | 0 | A fun animation, sent by a Nitro subscriber |
| BASIC | 1 | The standard animation |
#### Voice State Update
Sent when someone joins/leaves/moves voice channels. Inner payload is a [voice state](/developers/resources/voice#voice-state-object) object.
#### Voice Server Update
Sent when a guild's voice server is updated. This is sent when initially connecting to voice, and when the current voice instance fails over to a new server.
A null endpoint means that the voice server allocated has gone away and is trying to be reallocated. You should attempt to disconnect from the currently connected voice server, and not attempt to reconnect until a new voice server is allocated.
###### Voice Server Update Event Fields
| Field | Type | Description |
| --------- | --------- | ------------------------------------- |
| token | string | Voice connection token |
| guild\_id | snowflake | Guild this voice server update is for |
| endpoint | ?string | Voice server host |
###### Example Voice Server Update Payload
```json theme={"system"}
{
"token": "my_token",
"guild_id": "41771983423143937",
"endpoint": "sweetwater-12345.discord.media:2048"
}
```
### Webhooks
#### Webhooks Update
Sent when a guild channel's webhook is created, updated, or deleted.
###### Webhooks Update Event Fields
| Field | Type | Description |
| ----------- | --------- | ----------------- |
| guild\_id | snowflake | ID of the guild |
| channel\_id | snowflake | ID of the channel |
### Interactions
#### Interaction Create
Sent when a user uses an [Application Command](/developers/interactions/application-commands) or [Message Component](/developers/components/reference#component-object). Inner payload is an [Interaction](/developers/interactions/receiving-and-responding#interaction-object-interaction-structure).
### Stage Instances
#### Stage Instance Create
Sent when a [Stage instance](/developers/resources/stage-instance) is created (i.e. the Stage is now "live"). Inner payload is a [Stage instance](/developers/resources/stage-instance#stage-instance-object)
#### Stage Instance Update
Sent when a [Stage instance](/developers/resources/stage-instance) has been updated. Inner payload is a [Stage instance](/developers/resources/stage-instance#stage-instance-object)
#### Stage Instance Delete
Sent when a [Stage instance](/developers/resources/stage-instance) has been deleted (i.e. the Stage has been closed). Inner payload is a [Stage instance](/developers/resources/stage-instance#stage-instance-object)
### Subscriptions
#### Subscription Create
Subscription status should not be used to grant perks. Use [entitlements](/developers/resources/entitlement#entitlement-object) as an indication of whether a user should have access to a specific SKU. See our guide on [Implementing App Subscriptions](/developers/monetization/implementing-app-subscriptions) for more information.
Sent when a [Subscription](/developers/resources/subscription) for a Premium App is created. Inner payload is a [Subscription](/developers/resources/subscription#subscription-object).
A Subscription's `status` can be either **inactive** or **active** when this event is received. You will receive subsequent `SUBSCRIPTION_UPDATE` events if the `status` is updated to **active**. As a best practice, you should not grant any perks to users until the entitlements are created.
#### Subscription Update
Sent when a [Subscription](/developers/resources/subscription) for a Premium App has been updated. Inner payload is a [Subscription](/developers/resources/subscription#subscription-object) object.
#### Subscription Delete
Sent when a [Subscription](/developers/resources/subscription) for a Premium App has been deleted. Inner payload is a [Subscription](/developers/resources/subscription#subscription-object) object.
### Polls
#### Message Poll Vote Add
Sent when a user votes on a poll. If the poll allows multiple selection, one event will be sent per answer.
###### Message Poll Vote Add Fields
| Field | Type | Description |
| ----------- | --------- | ----------------- |
| user\_id | snowflake | ID of the user |
| channel\_id | snowflake | ID of the channel |
| message\_id | snowflake | ID of the message |
| guild\_id? | snowflake | ID of the guild |
| answer\_id | integer | ID of the answer |
#### Message Poll Vote Remove
Sent when a user removes their vote on a poll. If the poll allows for multiple selections, one event will be sent per answer.
###### Message Poll Vote Remove Fields
| Field | Type | Description |
| ----------- | --------- | ----------------- |
| user\_id | snowflake | ID of the user |
| channel\_id | snowflake | ID of the channel |
| message\_id | snowflake | ID of the message |
| guild\_id? | snowflake | ID of the guild |
| answer\_id | integer | ID of the answer |
### Rate Limits
#### Rate Limited
Sent when an app encounters a gateway rate limit for an event, such as [Request Guild Members](/developers/events/gateway-events#request-guild-members).
See changelog for [Introducing Rate Limit When Requesting All Guild Members](/developers/change-log#introducing-rate-limit-when-requesting-all-guild-members) for more information and timeline on this new rate limit.
###### Rate Limited Fields
| Field | Type | Description |
| ------------ | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| opcode | integer | [Gateway opcode](/developers/topics/opcodes-and-status-codes#gateway-gateway-opcodes) of the event that was rate limited |
| retry\_after | float | The number of seconds to wait before submitting another request |
| meta | [Rate Limit Metadata for Opcode](/developers/events/gateway-events#rate-limited-rate-limit-metadata-for-opcode-structure) | Metadata for the event that was rate limited |
###### Rate Limit Metadata for Opcode Structure
| Opcode | Type |
| ------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
| 8 | [Request Guild Member Rate Limit Metadata](/developers/events/gateway-events#rate-limited-request-guild-member-rate-limit-metadata-structure) |
###### Request Guild Member Rate Limit Metadata Structure
| Field | Type | Description |
| --------- | --------- | ----------------------------------------------------------------------------------------------------------- |
| guild\_id | snowflake | ID of the guild to get members for |
| nonce? | string | nonce to identify the [Guild Members Chunk](/developers/events/gateway-events#guild-members-chunk) response |
# Overview of Events
Source: https://docs.discord.com/developers/events/overview
Learn about the different ways apps can receive events from Discord.
Apps can listen to events happening in Discord to stay up-to-date with changes and updates to servers, users, and even your app. The following sections cover basic information about the different transport methods that can be used to receive events, and link out to relevant documentation.
## Receiving Events
There are many event types that can be accessed using different transport methods:
* **[Gateway events](/developers/events/overview#using-gateway)** are sent over a WebSocket connection between your app and Discord, and is the primary way to receive and send events. **Most events are only available via Gateway connections.**
* **[Webhook events](/developers/events/overview#using-webhooks)** are sent to your app's Webhook Event URL over HTTP.
* **[SDK events](/developers/events/overview#using-the-embedded-app-sdk)** are sent to your app when using the Embedded App SDK.
Read details about each way to receive events in the sections below.
### Using Gateway
Gateway events are the primary way apps can listen and send events. Most events related to resources in Discord, like updates to channels, guilds, roles, and messages, are only available over [Gateway](/developers/events/gateway).
Gateway events are sent over a WebSocket-based [Gateway connection](/developers/events/gateway#connections) between Discord and your app. To receive Gateway events, your app must open and maintain a persistent Gateway connection which you can read details about in the [Gateway documentation](/developers/events/gateway#connections). To make receiving Gateway events simpler, we suggest using a [developer library](/developers/developer-tools/community-resources#libraries) which helps setup, maintain, and handle common pitfalls with Gateway connections (like [rate limits](/developers/events/gateway#rate-limiting)).
Details about receiving events using the Gateway API is in the [Gateway documentation](/developers/events/gateway#receiving-events), and the full list of available Gateway events is in the [Gateway Events documentation](/developers/events/gateway-events).
### Using Webhooks
Webhook events let you receive a small number of events over HTTP. While many events aren't supported over HTTP, some events like [Application Authorized](/developers/events/webhook-events#application-authorized) (sent when your app is installed to a user or server) aren't available using other transport methods like Gateway.
Find the [list of webhook events](/developers/events/webhook-events#event-types) and details about subscribing and handling them in the [Webhook Events documentation](/developers/events/webhook-events).
### Using the Embedded App SDK
When developing [Activities](/developers/activities/overview), you can listen to a collection of [SDK events](/developers/developer-tools/embedded-app-sdk#sdk-events), like updates to a user's voice status or screen orientation. To listen to SDK events, you can call [`subscribe()`](/developers/developer-tools/embedded-app-sdk#subscribe) with the SDK event name.
Details about listening to events using the Embedded App SDK is in the [Embedded App SDK documentation](/developers/developer-tools/embedded-app-sdk).
## Next Steps
Gateway is the primary apps receive events on Discord. Read details about using the Gateway and receiving Gateway Events.
Read details about receiving and responding a small number of HTTP-based Webhook Events.
Read details about subscribing and receiving events specific to your Activity using the Embedded App SDK.
# Webhook Events
Source: https://docs.discord.com/developers/events/webhook-events
Learn how to receive Discord events over HTTP using webhooks.
Webhook events are one-way events sent to your app over HTTP to notify you when an event occurred. Unlike events that are [sent over Gateway connections](/developers/events/gateway), events sent over webhooks are not realtime or guaranteed to be in order.
While [incoming webhooks](/developers/resources/webhook) are triggered by an external service, webhook events (i.e. outgoing webhooks) are triggered by events happening in Discord. This means your app will need to set up a public URL where you can receive HTTP events, which is detailed in the [preparing for events](/developers/events/webhook-events#preparing-for-events) section.
## Subscribing to Events
To configure webhook events, you'll need to configure your URL and select the events you want your app to receive.
The steps below walk through subscribing using the developer portal. If you prefer to use the API, you can call [Edit Current Application](/developers/resources/application#edit-current-application).
In your [app's settings](https://discord.com/developers/applications), navigate to the [**Webhooks** page](https://discord.com/developers/applications/select/webhooks) from the left-hand sidebar then complete the following:
1. Under **Endpoint**, add a public URL that is set up to receive and acknowledge webhook events. Details about setting up a Webhook Events URL is in the [preparing for events](/developers/events/webhook-events#preparing-for-events) section.
2. Enable Events by clicking the toggle in the **Events** section.
3. Select the [webhook events](/developers/events/webhook-events#event-types) you want your app to receive.
4. Click **Save Changes**.
If your URL is successfully verified, your app should begin receiving the events you selected.
## Preparing for Events
To receive webhook events, you'll need to configure your app's **Webhook Event URL** in your app's settings.
### Configuring a Webhook Events URL
A **Webhook Events URL** is a public endpoint for your app where Discord can send your app HTTP-based events. If your app is using [Gateway events](/developers/events/gateway), you don't need to configure a Webhook Events URL.
#### Setting Up an Endpoint
Before you can add a Webhook Events URL to your app, your endpoint must be prepared for two things ahead of time:
1. Acknowledging `PING` events from Discord
2. Validate security-related request headers (`X-Signature-Ed25519` and `X-Signature-Timestamp`)
If either of these are not complete, your Webhook Events URL will not be validated. Details on acknowledging PING events and validating security-related headers are below.
###### Acknowledging PING requests
When adding your Webhook Events URL, Discord will send a `POST` request with a `PING` payload with a `type: 0` to your endpoint. Your app is expected to acknowledge the request by returning a `204` response with an empty body.
You must provide a valid `Content-Type` when responding to `PING`s. See [here](/developers/reference#http-api) for further information.
To properly acknowledge a `PING` payload, return a `204` response with no body:
```py theme={"system"}
@app.route('/', methods=['POST'])
def my_command():
if request.json["type"] == 0:
return Response(status=204)
```
###### Validating Security Request Headers
To receive events via HTTP, there are some security steps you **must** take before your app is eligible to receive requests.
Each webhook is sent with the following headers:
* `X-Signature-Ed25519` as a signature
* `X-Signature-Timestamp` as a timestamp
Using your favorite security library, you **must validate the request each time you receive an event**. If the signature fails validation, your app should respond with a `401` error code. Code examples of validating security headers is in the [Interactions documentation](/developers/interactions/overview#setting-up-an-endpoint-validating-security-request-headers).
In addition to ensuring your app validates security-related request headers at the time of saving your endpoint, Discord will also perform automated, routine security checks against your endpoint, including purposefully sending you invalid signatures. If you fail the validation, we will remove your Webhook Events URL and alert you via email and System DM.
We recommend checking out our [Community Resources](/developers/developer-tools/community-resources) and the libraries found there.
#### Adding an Webhook Events Endpoint URL
After you have a public endpoint to use as your app's Event Webhooks URL, you can add it to your app by going to your [app's settings](https://discord.com/developers/applications).
On the [**Webhooks** page](https://discord.com/developers/applications/select/webhooks), look for the **Endpoint URL** field. Paste your public URL that is set up to acknowledge `PING` messages and correctly handles security-related signature headers.
After you configure your Webhook Events URL, you can [enable and subscribe to events](/developers/events/webhook-events#subscribing-to-events) on the same page.
## Responding to Events
When your Webhook Event URL receives a webhook event, your app should respond with a `204` status code with no body **within 3 seconds** to acknowledge that your app successfully received it. If your app doesn't respond to the webhook event, Discord will retry sending it several times using exponential backoff for up to 10 minutes.
If your app fails to respond too often, Discord will stop sending you webhook events and notify you via email.
## Webhook Event Payloads
Webhook events are wrapped in an outer payload, with an inner `event` object.
### Payload Structure
Structure of the outer webhook payload
| Field | Type | Description |
| --------------- | ------------------------------------------------------------------------ | -------------------------------------------------------------- |
| version | integer | Version scheme for the webhook event. Currently always `1` |
| application\_id | snowflake | ID of your app |
| type | [webhook type](/developers/events/webhook-events#webhook-types) | Type of webhook, either `0` for PING or `1` for webhook events |
| event? | [event body](/developers/events/webhook-events#event-body-object) object | Event data payload |
#### Webhook Types
| Type | Value | Description |
| ----- | ----- | ------------------------------------------------------------------------------------------------------------- |
| PING | `0` | PING event sent to verify your Webhook Event URL is active |
| Event | `1` | Webhook event (details for event in [event body](/developers/events/webhook-events#event-body-object) object) |
#### Event Body Object
The event body contains high-level data about the event, like the type and time it was triggered.
The inner `data` object contains information specific to the [event type](/developers/events/webhook-events#event-types).
| Field | Type | Description |
| --------- | ------ | -------------------------------------------------------------------------------------------------------- |
| type | string | [Event type](/developers/events/webhook-events#event-types) |
| timestamp | string | Timestamp of when the event occurred in [ISO8601 format](/developers/reference#iso8601-datetime) |
| data? | object | Data for the event. The shape depends on the [event type](/developers/events/webhook-events#event-types) |
## Event Types
The table below includes the different webhook event types your app can subscribe to.
The "Value" column corresponds to the event's `type` field value in the [event body object](/developers/events/webhook-events#event-body-object).
| Name | Value | Description |
| ------------------------------------------------------------------------------------------ | ---------------------------- | ------------------------------------------------------------------------- |
| [Application Authorized](/developers/events/webhook-events#application-authorized) | `APPLICATION_AUTHORIZED` | Sent when an app was authorized by a user to a server or their account |
| [Application Deauthorized](/developers/events/webhook-events#application-deauthorized) | `APPLICATION_DEAUTHORIZED` | Sent when an app was deauthorized by a user |
| [Entitlement Create](/developers/events/webhook-events#entitlement-create) | `ENTITLEMENT_CREATE` | Entitlement was created |
| [Entitlement Update](/developers/events/webhook-events#entitlement-update) | `ENTITLEMENT_UPDATE` | Entitlement was updated |
| [Entitlement Delete](/developers/events/webhook-events#entitlement-delete) | `ENTITLEMENT_DELETE` | Entitlement was deleted |
| [Quest User Enrollment](/developers/events/webhook-events#quest-user-enrollment) | `QUEST_USER_ENROLLMENT` | User was added to a Quest (currently unavailable) |
| [Lobby Message Create](/developers/events/webhook-events#lobby-message-create) | `LOBBY_MESSAGE_CREATE` | Sent when a message is created in a lobby |
| [Lobby Message Update](/developers/events/webhook-events#lobby-message-update) | `LOBBY_MESSAGE_UPDATE` | Sent when a message is updated in a lobby |
| [Lobby Message Delete](/developers/events/webhook-events#lobby-message-delete) | `LOBBY_MESSAGE_DELETE` | Sent when a message is deleted from a lobby |
| [Game Direct Message Create](/developers/events/webhook-events#game-direct-message-create) | `GAME_DIRECT_MESSAGE_CREATE` | Sent when a direct message is created during an active Social SDK session |
| [Game Direct Message Update](/developers/events/webhook-events#game-direct-message-update) | `GAME_DIRECT_MESSAGE_UPDATE` | Sent when a direct message is updated during an active Social SDK session |
| [Game Direct Message Delete](/developers/events/webhook-events#game-direct-message-delete) | `GAME_DIRECT_MESSAGE_DELETE` | Sent when a direct message is deleted during an active Social SDK session |
#### Application Authorized
`APPLICATION_AUTHORIZED` is sent when the app is added to a server or user account.
###### Application Authorized Structure
| Field | Type | Description |
| ------------------ | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| integration\_type? | integer | [Installation context](/developers/resources/application#application-object-application-integration-types) for the authorization. Either guild (`0`) if installed to a server or user (`1`) if installed to a user's account |
| user | [user object](/developers/resources/user#user-object-user-structure) | User who authorized the app |
| scopes | array of strings | List of [scopes](/developers/topics/oauth2#shared-resources-oauth2-scopes) the user authorized |
| guild? | [guild object](/developers/resources/guild#guild-object-guild-structure) | Server which app was authorized for (when integration type is `0`) |
###### Application Authorized Example
```json theme={"system"}
{
"version": 1,
"application_id": "1234560123453231555",
"type": 1,
"event": {
"type": "APPLICATION_AUTHORIZED",
"timestamp": "2024-10-18T14:42:53.064834",
"data": {
"integration_type": 1,
"scopes": [
"applications.commands"
],
"user": {
// user data
}
}
}
}
```
#### Application Deauthorized
`APPLICATION_DEAUTHORIZED` is sent when the app is deauthorized by a user.
###### Application Deauthorized Structure
| Field | Type | Description |
| ----- | -------------------------------------------------------------------- | ----------------------------- |
| user | [user object](/developers/resources/user#user-object-user-structure) | User who deauthorized the app |
###### Application Deauthorized Example
```json theme={"system"}
{
"version": 1,
"application_id": "1234560123453231555",
"type": 1,
"event": {
"type": "APPLICATION_DEAUTHORIZED",
"timestamp": "2024-10-18T14:42:53.064834",
"data": {
"user": {
// user data
}
}
}
}
```
###### Discord Social SDK
For [Discord Social SDK](/developers/discord-social-sdk) apps, `APPLICATION_DEAUTHORIZED` is the **only out-of-game signal that a user's link state has changed**. Listening for it is required to keep your application state in sync — if you miss the webhook, the [next token use will fail](/developers/discord-social-sdk/development-guides/account-linking-with-discord#when-refresh-fails) and the game should fall back to the [provisional account flow](/developers/discord-social-sdk/development-guides/using-provisional-accounts#implementing-provisional-accounts).
Every revocation that fires this event is mechanically an [unmerge](/developers/discord-social-sdk/development-guides/using-provisional-accounts#unmerging-provisional-accounts) — the merged Discord account reverts to a new provisional account, and the OAuth2 tokens for the Discord user become invalid. The triggers include:
* Token revocations via [`/oauth2/token/revoke`](/developers/topics/oauth2#authorization-code-grant) or `Client::RevokeToken`.
* The user removing your app from *User Settings → Authorized Apps* in Discord.
* The [Social SDK unmerge endpoints](/developers/discord-social-sdk/development-guides/using-provisional-accounts#unmerging-provisional-accounts).
* The user's Discord [account being banned](/developers/discord-social-sdk/development-guides/using-provisional-accounts#ban-driven-unmerge).
#### Entitlement Create
`ENTITLEMENT_CREATE` is sent when an [entitlement](/developers/resources/entitlement) is created when a user purchases or is otherwise granted one of your app's SKUs. Refer to the [Monetization documentation](/developers/monetization/overview) for details.
###### Entitlement Create Structure
The inner payload is an [entitlement](/developers/resources/entitlement#entitlement-object) object.
###### Entitlement Create Example
```json theme={"system"}
{
"version": 1,
"application_id": "1234560123453231555",
"type": 1,
"event": {
"type": "ENTITLEMENT_CREATE",
"timestamp": "2024-10-18T18:41:21.109604",
"data": {
"application_id": "1234560123453231555",
"consumed": false,
"deleted": false,
"gift_code_flags": 0,
"id": "1234505980407808808",
"promotion_id": null,
"sku_id": "123489045643835123",
"type": 4,
"user_id": "111178765189277770"
}
}
}
```
#### Entitlement Update
`ENTITLEMENT_UPDATE` is sent when an [entitlement](/developers/resources/entitlement) is updated. Refer to the [Monetization documentation](/developers/monetization/overview) for details.
###### Entitlement Update Structure
The inner payload is an [entitlement](/developers/resources/entitlement#entitlement-object) object.
###### Entitlement Update Example
```json theme={"system"}
{
"version": 1,
"application_id": "1234560123453231555",
"type": 1,
"event": {
"type": "ENTITLEMENT_UPDATE",
"timestamp": "2024-10-18T18:41:21.109604",
"data": {
"application_id": "1234560123453231555",
"consumed": false,
"deleted": false,
"gift_code_flags": 0,
"id": "1234505980407808808",
"promotion_id": null,
"sku_id": "123489045643835123",
"type": 4,
"user_id": "111178765189277770"
}
}
}
```
#### Entitlement Delete
`ENTITLEMENT_DELETE` is sent when an [entitlement](/developers/resources/entitlement) is deleted. Refer to the [Monetization documentation](/developers/monetization/overview) for details.
###### Entitlement Delete Structure
The inner payload is an [entitlement](/developers/resources/entitlement#entitlement-object) object.
###### Entitlement Delete Example
```json theme={"system"}
{
"version": 1,
"application_id": "1234560123453231555",
"type": 1,
"event": {
"type": "ENTITLEMENT_DELETE",
"timestamp": "2024-10-18T18:41:21.109604",
"data": {
"application_id": "1234560123453231555",
"consumed": false,
"deleted": true,
"gift_code_flags": 0,
"id": "1234505980407808808",
"promotion_id": null,
"sku_id": "123489045643835123",
"type": 4,
"user_id": "111178765189277770"
}
}
}
```
#### Quest User Enrollment
This event cannot be received by apps at this time. It's documented because it appears on the Webhooks settings page.
`QUEST_USER_ENROLLMENT` is sent when a user is added to a Quest on Discord.
#### Lobby Message Create
`LOBBY_MESSAGE_CREATE` is sent when a message is created in a lobby.
###### Lobby Message Create Structure
The inner payload is a [lobby message object](/developers/events/webhook-events#lobby-message-object).
###### Lobby Message Create Example
```json theme={"system"}
{
"version": 1,
"application_id": "1234567765431056709",
"type": 1,
"event": {
"type": "LOBBY_MESSAGE_CREATE",
"timestamp": "2024-10-18T18:41:21.109604",
"data": {
"id": "1397729799727878254",
"type": 0,
"content": "welcome to the party!",
"lobby_id": "1397729744753266719",
"channel_id": "1397729744753266719",
"author": {
// user data
},
"flags": 65536,
"application_id": "1234567765431056709"
}
}
}
```
#### Lobby Message Update
`LOBBY_MESSAGE_UPDATE` is sent when a message is updated in a lobby.
###### Lobby Message Update Structure
The inner payload is a [lobby message object](/developers/events/webhook-events#lobby-message-object) with additional fields for message updates.
###### Lobby Message Update Example
```json theme={"system"}
{
"version": 1,
"application_id": "1234567765431056709",
"type": 1,
"event": {
"type": "LOBBY_MESSAGE_UPDATE",
"timestamp": "2025-08-05T20:39:19.587872",
"data": {
"id": "1402390388030832792",
"type": 0,
"content": "noice",
"lobby_id": "1402385687281537066",
"channel_id": "1402389638311841883",
"author": {
// user data
},
"edited_timestamp": "2025-08-05T20:39:19.557970+00:00",
"flags": 0,
"timestamp": "2025-08-05T20:38:43.660000+00:00"
}
}
}
```
#### Lobby Message Delete
`LOBBY_MESSAGE_DELETE` is sent when a message is deleted from a lobby.
###### Lobby Message Delete Structure
| Field | Type | Description |
| --------- | --------- | --------------------------------------------- |
| id | snowflake | ID of the deleted message |
| lobby\_id | snowflake | ID of the lobby where the message was deleted |
###### Lobby Message Delete Example
```json theme={"system"}
{
"version": 1,
"application_id": "1234567765431056709",
"type": 1,
"event": {
"type": "LOBBY_MESSAGE_DELETE",
"timestamp": "2025-08-05T21:44:09.412957",
"data": {
"id": "1402406637632884857",
"lobby_id": "1402399883394285659"
}
}
}
```
#### Game Direct Message Create
`GAME_DIRECT_MESSAGE_CREATE` is sent when a direct message is created while at least one user has an active Social SDK session.
###### Game Direct Message Create Structure
The inner payload is a [message object](/developers/events/webhook-events#message-object) or [SDK DM message object](/developers/events/webhook-events#sdk-dm-message-object).
###### Game Direct Message Create Example
```json theme={"system"}
{
"version": 1,
"application_id": "1234567765431056709",
"type": 1,
"event": {
"type": "GAME_DIRECT_MESSAGE_CREATE",
"timestamp": "2025-08-14T18:09:38.063234",
"data": {
"id": "1405614357781545021",
"type": 0,
"content": "get in friend, we're going raiding",
"channel_id": "1405604229820715098",
"author": {
// user data
},
"timestamp": "2025-08-14T18:09:37.947000+00:00",
"application_id": "1234567765431056709",
"attachments": []
}
}
}
```
#### Game Direct Message Update
`GAME_DIRECT_MESSAGE_UPDATE` is sent when a direct message is updated while at least one user has an active Social SDK session.
###### Game Direct Message Update Structure
The inner payload is a [message object](/developers/events/webhook-events#message-object) or [SDK DM message object](/developers/events/webhook-events#sdk-dm-message-object).
###### Game Direct Message Update Example
```json theme={"system"}
{
"version": 1,
"application_id": "1234567765431056709",
"type": 1,
"event": {
"type": "GAME_DIRECT_MESSAGE_UPDATE",
"timestamp": "2025-08-14T16:44:31.847073",
"data": {
"id": "1405591838810706081",
"content": "almost ready to queue?",
"channel_id": "1404960877324533784",
"author": {
// user data
},
"recipient_id": "1404960877324533784"
}
}
}
```
#### Game Direct Message Delete
`GAME_DIRECT_MESSAGE_DELETE` is sent when a direct message is deleted while at least one user has an active Social SDK session.
###### Game Direct Message Delete Structure
The inner payload is a [message object](/developers/events/webhook-events#message-object) or [SDK DM message object](/developers/events/webhook-events#sdk-dm-message-object).
###### Game Direct Message Delete Example
```json theme={"system"}
{
"version": 1,
"application_id": "1234567765431056709",
"type": 1,
"event": {
"type": "GAME_DIRECT_MESSAGE_DELETE",
"timestamp": "2025-08-20T17:01:50.099204",
"data": {
"id": "1407771600643686503",
"type": 0,
"content": "cant make it in time",
"channel_id": "1405604229820715098",
"author": {
// user data
},
"timestamp": "2025-08-20T17:01:44.725000+00:00",
"flags": 0,
"attachments": [],
"components": []
}
}
}
```
## Social SDK Message Objects
Discord Social SDK utilizes specialized message objects for lobby and direct message events that occur during active game sessions. These objects extend or modify the standard Discord message structure to support communication features.
* [Lobby messages](/developers/events/webhook-events#lobby-message-object) include lobby-specific fields like `lobby_id`
* [Standard Discord messages](/developers/events/webhook-events#message-object) in SDK contexts may include additional fields
* [SDK DM messages](/developers/events/webhook-events#sdk-dm-message-object) are used for communication between provisional accounts
These objects are used in the webhook events `LOBBY_MESSAGE_*` and `GAME_DIRECT_MESSAGE_*` depending on the messaging context.
### Lobby Message Object
Represents a message sent in a lobby or [Linked Channel](/developers/discord-social-sdk/development-guides/linked-channels).
###### Lobby Message Structure
| Field | Type | Description |
| ---------------- | -------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| id | snowflake | ID of the message |
| type | integer | [Type of message](/developers/resources/message#message-object-message-types) |
| content | string | Contents of the message |
| lobby\_id | snowflake | ID of the lobby where the message was sent |
| channel\_id | snowflake | ID of the channel the message was sent in |
| author | [user object](/developers/resources/user#user-object-user-structure) | Author of the message |
| metadata? | object | Additional metadata for the message (key-value pairs) |
| flags | integer | [Message flags](/developers/resources/message#message-object-message-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) |
| application\_id? | snowflake | ID of the application (only present during active Social SDK sessions) |
### Message Object
Standard [Message Object](/developers/resources/message#message-object) with additional fields.
###### Additional Fields
| Field | Type | Description |
| ---------- | -------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| lobby\_id? | snowflake | ID of the lobby where the message was created (only present in Linked Channel messages) |
| channel | [channel object](/developers/resources/channel#channel-object) | Channel object with recipient information |
### SDK DM Message Object
Represents a message between provisional users that exists only in-game.
###### SDK DM Message Structure
| Field | Type | Description |
| --------------- | -------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| id | snowflake | ID of the message |
| type | integer | [Type of message](/developers/resources/message#message-object-message-types) |
| content | string | Contents of the message |
| author | [user object](/developers/resources/user#user-object-user-structure) | Author of the message |
| flags | integer | [Message flags](/developers/resources/message#message-object-message-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) |
| application\_id | snowflake | ID of the application that created the message |
| channel | [channel object](/developers/resources/channel#channel-object) | Channel object with recipient information |
| activity? | [message activity](/developers/resources/message#message-object-message-activity-structure) object | Sent with Rich Presence-related chat embeds |
| application? | partial [application](/developers/resources/application#application-object) object | Sent with Rich Presence-related chat embeds |
When both users in a direct message are provisional accounts, messages become "SDK DM messages" that are only visible in-game and use this specialized structure.
# How Can I Grow My Game?
Source: https://docs.discord.com/developers/game-development/how-to-grow-your-game
Building and publishing a game is hard. Getting players to discover, share, and play it is even harder. With dozens of incredibly strong games released each day, some with huge marketing budgets, it's harder than ever to build a player base and keep it strong. That doesn't mean it's impossible to do though, and Discord has a set of tools to help you.
**Why Discord?**
Discord has over 90 million daily active users\* and over 90% of Discord users play video games\*. Discord is already where massive communities of gamers connect across desktop, console, and mobile. Take advantage of those connections to drive into deeper player engagement, longer gaming sessions, and organic growth for your game.
66% of Discord users play games with friends weekly\*. Discord users share the games they play with their friends. Your players are your biggest cheerleaders, make it easy for them to share what they're playing.
Discord PC players played a median of 6 times longer if they were playing with at least one friend on Discord, vs playing alone\*. Creating social experiences that players can share with their friends keeps games fresh for longer. A social game doesn't necessarily have to be a multiplayer experience. Consider daily puzzle games that allow score sharing or roguelikes that have daily runs and leaderboards. Both offer unique opportunities for community engagement and friendly rivalries.
Using Discord as part of your game development strategy can help build community, grow your game, and keep players playing!
\**Discord Internal Data, 2025*
***
# Build a Community to Capture Your Players
Whether you're just starting on a new game or you've already got a beta up and running, it's never too early to think about community. Discord is a leading platform for hosting official game communities; over 9,000 official game communities are on Discord, with over 80 million members\*. Players who join your game’s Discord community can develop a sense of belonging and connection with your game that goes beyond just playing. They become invested in your game's success, provide valuable feedback, and naturally bring new players into the community. It also gives you a chance to interact directly with your players, get to know them better, provide feature updates and bug fixes, and learn why they love your game. A thriving community with active developers gives players a reason to stick around between updates and creates lasting relationships that keep your game alive.
\**Discord Internal Data, 2025*
Set up a community server, design roles and channels, and bring players in from your game
***
# Get Your Game Seen With Rich Presence
Have you ever seen what a friend is playing in the Discord client? This is called Rich Presence and there are so many ways to use it to get your game seen. The [Discord Social SDK](https://discord.com/developers/social-sdk) allows you to control how Rich Presence is displayed to your players in Discord by adding text, images, links, buttons, and more. You can even add invites that allow players to join your game directly from Discord. With Rich Presence, your player’s profiles become organic marketing for your game and give you a seamless way to create social experiences. The easier you make it for player’s to show off what they’re playing the faster your game will naturally spread through their social graph.
Implement Rich Presence with game details, lobby joining, game launching, and store links
***
# Keeping Your Players Engaged
A Discord bot or companion app keeps your game present in player’s lives even when they're not actively playing. You can use them to bring your game’s world into Discord, announce in-game events, share achievements and leaderboards, and run giveaways or promos. This keeps your game top-of-mind in Discord and gives players a reason to jump back in.
> **“Apps weren’t just background tools; many existed as in-world systems or characters, quietly turning routine Discord behavior into part of the ARG itself.”**
*[Case study from Sabotage Studio](https://discord.com/developer-case-studies/sabotage). Read more to learn about how they used community and bots to fill the gap between game updates.*
Bots and companion apps can extend your game's experience into Discord, creating engagement loops that bring players back. A guild-installable bot can live in your community server and user-installable bots allow players to use them anywhere for optimal sharing. A well-built bot helps players organically share your game and community. Players sharing their high scores, leaderboards, builds, and achievements through your bot can drive their friends to return to the game just to beat them, or even purchase the game to join in the competition. It's re-engagement and community building that works for you around the clock.
Build a bot connected to your game with leaderboards, event announcements, and achievement sharing
***
# Which Strategy Should You Start With?
You don't need to implement everything here at once. Pick a strategy that matches your current development state and player base, then build from there!
## Pre-launch or early development:
Start with "**[How Do I Create a Community For My Game?](/developers/game-development/how-to-create-a-community-for-your-game)**". Create a home for your early testers and first players to join. A community built during development gives you valuable feedback and creates a core group of invested players who will champion your game at launch.
## Existing game:
Start with "**[How Can I Get My Game Seen?](/developers/game-development/how-to-get-your-game-seen)**". Rich Presence and the Social SDK make it easy for players to see what their friends are playing and buy the game or join them right from Discord. This helps create organic growth through friend groups.
## Live game with an existing community:
Start with "**[How Do I Keep My Players Engaged?](/developers/game-development/how-to-keep-your-players-engaged)**". Build a bot that bridges your game and Discord. Think about what information from your game could be the most helpful thing for player’s to share. It can celebrate achievements, announce events, and build competition through leaderboards. This strengthens the connection between your community and your game.
Set up a community server, design roles and channels, and bring players in from your game
Implement Rich Presence with game details, lobby joining, game launching, and store links
Build a bot connected to your game with leaderboards, event announcements, and achievement sharing
# Getting Started with Privileged Intent Review
Source: https://docs.discord.com/developers/gateway/getting-started-with-privileged-intent-review
Guide to understanding the privileged intent review process and how to submit your application for review.
So you got a notification about [Privileged Intents](/developers/events/gateway#privileged-intents). Maybe your app is growing and you've hit the review threshold, maybe you need to reapply for access you already have, or maybe something in your app just stopped working, and you're trying to figure out why. This guide will help walk you through what's happening, what it means for your app, and what to do next.
***
## What are Privileged Intents?
Discord gates access to certain data behind [Privileged Intents](/developers/events/gateway#privileged-intents). Currently, there are three of them:
* **Guild Presences**: user online/offline status, activities, and platform info
* **Guild Members**: member join/leave/update events and the ability to list all members in a server
* **Message Content**: the `content`, `embeds`, `attachments`, `components`, and `poll` fields in message objects
These intents are disabled by default due to the nature of their data and should only be enabled as needed. If your app doesn't have them enabled, it simply won't have access to the data they grant.
***
## What's changed?
As of June 10th, 2026, we've made some changes to the Privileged Intents review process. [Read the original announcement here](https://support-dev.discord.com/hc/articles/40281523410967) for more information.
### 1. The review threshold is now based on users, not servers
Previously, apps in fewer than 100 servers could access Privileged Intents by toggling them on in the Developer Portal, and apps in 100+ servers needed to apply for access. That threshold is now based on the number of unique users who can see your app across all the servers it's in. If that number exceeds 10,000, your app needs to apply for Privileged Intent access.
This means a bot in 50 large servers could hit the threshold, while a bot in 200 small servers might not. This change focuses on how many users your app reaches, not how many servers it's installed in.
### 2. Apps must reapply annually for continued access
Apps that already have Privileged Intent access granted from a prior review must reapply each year through the Developer Portal for their continued access. You'll receive advance notice before your reapplication date, so this shouldn't sneak up on you. This process allows you to update and keep your use case documentation current to demonstrate your continued need for access.
### 3. Apps can continue to use intents and join servers while in review
While in review for privileged intent access, your app will continue to function with the intents you are requesting access to. Your app may also continue to grow and join new servers during this time.
***
## Step 1: Figure out where your app stands
We count the number of unique users who have access to your app across all the servers it's installed in.
### Under 10,000 users
You can turn privileged intents on and off from the Developer Portal as needed. No need to apply for access. Go to your app's **Bot** page, scroll to **Privileged Gateway Intents**, and toggle on what your app needs to function.
Keep in mind: even though you *can technically* enable these intents right now, you may only enable what you actually need. If your app grows past the 10,000 user threshold, you'll also need to submit for review with information about your use case for each intent demonstrating that the access is required. Building with the minimum set of intents from the start means less work later as well.
To learn more about scenarios in which you won't need a privileged intent, see our [You Might Not Need a Privileged Intent](/developers/gateway/you-might-not-need-a-privileged-intent) guide.
### Approaching or over 10,000 users
If your app reaches the 10,000-user threshold and has any Privileged Intent enabled (or attempts to enable a Privileged Intent), you'll receive a notification in the Developer Portal letting you know it's time to apply for access. You will have **90 days** from the date of the notification to apply. Your app can continue joining servers and reaching new users while its submission is under review. If you don't apply during this window, your app's current Privileged Intents access will be removed, but you can still apply at any time to request access again.
### Access already granted
If your app already has Privileged Intent access granted from a prior review, you're good for now, but you will need to reapply annually.
You will receive a notification when it's time to reapply. After you receive that notification, you will have 90 days to reapply for review of your app's continued access to the Privileged Intents. If access is granted, you will receive a notification before your next annual review. Keep track of your use cases for each intent as they may evolve over time so the review process goes smoothly when the time comes for you to submit the latest information. If you don't apply during the 90-day window, your app's current Privileged Intents access will be removed, but you can still apply at any time to request access again.
***
## Step 2: Audit what Privileged Intents your app uses
Before you apply for anything, take stock of what data your bot actually needs. This is the most important step, and skipping it is the most common mistake developers make. Review your app to make sure its use case requires Privileged Intent access and it is consistent with our [Developer Policy](https://support-dev.discord.com/hc/en-us/articles/8563934450327-Discord-Developer-Policy) and [Developer Terms of Service](https://support-dev.discord.com/hc/en-us/articles/8562894815383-Discord-Developer-Terms-of-Service). Follow our [You Might Not Need a Privileged Intent](/developers/gateway/you-might-not-need-a-privileged-intent) guide and assess your use case for alternatives for each intent.
***
## Step 3: Decide whether to adopt an alternative or apply for access
For each intent your app is using, you have two paths:
### Path A: Remove the dependency
If you can accomplish the same thing without the Privileged Intent, choose this path. This is also less work in the long run because you won't need to submit for review demonstrating that the access is required, you won't need to reapply annually, and you won't risk losing access.
Common migrations:
* **Text commands to slash commands**: This is the most common reason developers request the Message Content privileged intent. [Slash commands](/developers/interactions/application-commands) are the recommended replacement and don't require any privileged intents.
* **Full member list to on-demand lookups**: If your app caches every server member but only ever looks up a few, switch to fetching individual members via the API when you need them.
Check out our [You Might Not Need a Privileged Intent](/developers/gateway/you-might-not-need-a-privileged-intent) guide for more alternatives.
### Path B: Apply for Privileged Intent access
If your bot genuinely needs the data that Privileged Intents provide for a compliant use case and there's no alternative, you can apply for access. Here's how to set yourself up for success with your submission:
**Be specific about your use case:** "My bot needs message content" is not a justification. "My bot provides automated content moderation by scanning messages for phishing links and known scam patterns" is a better example. Explain what your bot does with the data and why interactions can't replace it.
**Only request what you need:** If your bot needs Message Content for moderation but doesn't need Guild Presences for anything, don't request Guild Presences. Requesting intents you can't demonstrate your bot needs hurts your submission.
**Explain how you handle the data responsibly:** If you're storing the data, explain why it's necessary and describe your retention policy and security measures. If you're processing it in memory and discarding it, say so.
**Think about annual review from the start:** Since you'll need to reapply annually, write your responses in a way that are easy to understand and update. Keep records of how your bot uses the data as it may evolve so you have it on hand when review time comes around.
**Ensure the information you submit is clear, accurate, and complete. Otherwise it may result in review delays or denial of your request.**
If access is granted, you can enable the relevant intents for your app as described in Step 4.
***
## Step 4: Enable the Intent in Dev Portal and Your Code
There are **two places you need to enable a privileged intent if you have access**, and missing either one will throw an error.
### 1. The Developer Portal
Go to the Developer Portal, select your app, [click the **Bot** tab](https://discord.com/developers/applications/select/bot), and scroll to **Privileged Gateway Intents**. Toggle on the intents you need (and have been granted access for, if your app's access required review).
### 2. Your code
Enabling the intent in the portal tells Discord your app is *technically allowed* to receive the data. You also need to *request* it when your bot connects to the Gateway. How you do this depends on your library, or learn more about defining Gateway Intents from scratch.
If you enable an intent in your code but not in the portal (or vice versa), you'll get a `[DisallowedIntents]` error when your app tries to connect.
***
## Step 5: Test and verify
After making changes, confirm everything works:
1. **Restart your bot.** Intent changes only take effect on a new Gateway connection. A running bot won't pick up portal changes until it reconnects.
2. **Check your error logs.** Look for `DisallowedIntents` errors, which mean there's a mismatch between your code and your portal settings.
3. **Test affected features.** If you removed a Privileged Intent, make sure the features that depended on it still work with their new implementation. If you migrated prefix commands to slash commands, verify they're registered and responding correctly.
4. **Test with a second account.** Some things behave differently depending on whether the message author is the bot itself. Use a separate Discord account to test interactions as a real user would.
***
## Common situations and what to do
### "My bot's commands stopped working"
If your bot uses prefix commands (like `!help`) and you were relying on the Message Content intent, your bot can no longer read the content of messages. It receives the `MESSAGE_CREATE` event, but the `content` field is empty, so it never sees the prefix.
**Fix:** Migrate to [slash commands](developers/interactions/application-commands).
### "My welcome messages stopped firing"
For a bot to send a message in real-time when new members join, it needs to be able to receive `GUILD_MEMBER_ADD` events.
**Fix:** Enable and request the Guild Members intent demonstrating the access is required for your app's stated functionality.
### "I got a DisallowedIntents error"
Your code is requesting an intent that isn't enabled in the Developer Portal, or that your bot hasn't been granted access for.
**Fix:** Check the portal. If the toggle is off, turn it on as needed. If your app is over the 10,000 user threshold and the toggle is locked, you need to apply for Privileged Intent access.
### "My intent application was denied"
Requests may be denied when the information provided in your submission does not demonstrate that the stated use case requires access (e.g., can be accomplished through interactions or other means without privileged intents) or is otherwise not consistent with our requirements (e.g., the submission is unclear or incomplete; the described use case violates our policies). In many cases, you may be given additional time to resubmit with updated information or make necessary changes while your app retains its current access. In other cases, your access will be removed but you can still submit a new request at any time. Please see the notice we sent you for details.
**Fix:** Re-evaluate whether you truly need the intent and the use case is consistent with our requirements. If you do, submit a request with more specific and complete information and include evidence (screenshots, video) of the feature in action demonstrating that the access is required. If the feature can be rebuilt using slash commands, components, or modals, or other alternatives, do that instead and drop the intent request. We've put together a guide to help you understand each intent and potential alternatives.
### "I already have access granted, and it's been a year or more than a year"
With the annual review requirement, you'll receive advance notice when it's time to reapply. When it's time, go to the Developer Portal and submit your request through the same process as the initial application. If you haven't received a notice yet and think you are due for an annual review, please wait until you receive the notice indicating that it's time to re-apply.
### "I'm not sure if I need an intent or not"
Follow our companion guide, [You Might Not Need a Privileged Intent](/developers/gateway/you-might-not-need-a-privileged-intent), which walks through each intent with decision checklists and alternatives, and assess whether your app's use case needs access.
# You Might Not Need a Privileged Intent
Source: https://docs.discord.com/developers/gateway/you-might-not-need-a-privileged-intent
Guide to understanding privileged intents and their alternatives for your Discord app.
[Privileged Intents](/developers/events/gateway#privileged-intents) give your Discord app access to additional data from servers they're added to, which can be useful when building your app, but they also come with extra review requirements and responsibility. Before you reach for one, ask yourself:
> Do I actually need this?
**This guide walks through each privileged intent to help you determine whether you truly need it or whether an alternative approach will work.**
Many developers may enable privileged intents out of habit or because a tutorial told them to, only to later discover that our API offers alternatives that accomplish many of the same goals without the overhead.
As a reminder, developers may access only the data necessary for their app's functionality. We encourage you to review our [Developer Policy](https://support-dev.discord.com/hc/en-us/articles/8563934450327-Discord-Developer-Policy) and [Developer Terms of Service](https://support-dev.discord.com/hc/en-us/articles/8562894815383-Discord-Developer-Terms-of-Service), along with this guide, to make sure your app and its use of intended privileged intents are consistent with those policies.
### Who does this apply to?
All apps that are considering or have already enabled privileged intents. Apps with server-installed user counts fewer than 10,000 can toggle privileged intents in the Developer Portal.
Once your app grows past 10,000 users, you'll need to apply for continued access and share information about your app and intended use cases for the requested intents. Planning ahead saves you from a migration later.
***
## The Three Privileged Intents
| Intent | What it controls |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Guild Presences** | Access to presence update events: when users go online/offline, change their status, or update their activity via Rich Presence. |
| **Guild Members** | Access to guild member events (joins, updates, leaves) and the ability to request full member lists. |
| **Message Content** | Access to message content fields (`content`, `embeds`, `attachments`, `components`, `poll`) across Gateway events and API responses. Unlike the other two, this isn't tied to specific events. Instead, it controls data within message objects everywhere. |
## Guild Members Intent
### What you get with the intent
The Guild Members intent unlocks three categories of functionality:
* **Member events via the Gateway:** The `GUILD_MEMBER_ADD`, `GUILD_MEMBER_UPDATE`, and `GUILD_MEMBER_REMOVE` events fire when members join, change their profile/roles, or leave a server.
* **Full member list:** The ability to request a complete list of every member in a server via the Gateway with the [`Request Guild Members` opcode](/developers/events/gateway-events#request-guild-members) or the [List Guild Members API](/developers/resources/guild#list-guild-members) endpoint.
### What you can do without the intent
You might not need the full member list or real-time member events. Our API provides several REST endpoints that work **without** the Guild Members intent:
* **[Get Guild Member](/developers/resources/guild#get-guild-member):** Fetch a single member by their user ID. If you know *who* you're looking for, you don't need to enumerate every member.
* **[Search Guild Members](/developers/resources/guild#search-guild-members):** Search for members by username or nickname prefix. Great for autocomplete, lookup commands, or finding specific users.
* **[Get User](/developers/resources/user#get-user):** Get basic user information (username, avatar, etc.) without any guild-specific context.
* **[Interaction-provided member data](/developers/interactions/receiving-and-responding#message-interaction-object):** When a user triggers a slash command, button, select menu, or modal, our API includes the member object in the interaction payload. You get their roles, nickname, permissions, and more, with no intent required.
### Decision checklist
Ask yourself these questions:
1. Do I need to know about every member, or just specific ones I can look up by ID or search query?
2. Do I need real-time events when members join, leave, or update, or can I fetch member data on demand when a user interacts with my bot using interactions?
If your answers lean toward "specific ones" and "on demand," you probably don't need this intent since the REST API and interaction payloads cover those use cases well.
***
## Guild Presences Intent
### What you get with the intent
The Guild Presences intent gives you access to `PRESENCE_UPDATE` events, which include:
* `status`: Whether the user is online, idle, do not disturb, or offline.
* `activities`: What the user is currently playing, streaming, listening to, or watching (Rich Presence data).
* `client_status`: Which platform(s) the user is active on (desktop, mobile, web).
### What you can do without the intent
This data is not available through any other means. Before building your app, you should consider whether you actually need this real-time presence data about users.
Consider these endpoints that work without the Guild Presences intent:
* **[Get User](/developers/resources/user#get-user):** You can fetch a user's basic profile information (username, avatar, banner) without the intent. This covers many "user info" command scenarios.
* **[Guild Object - approximate\_presence\_count](/developers/resources/guild#get-guild):** Available on the Guild object via the API. Tells you roughly how many members are online without accessing individual presences.
### Decision checklist
1. Does my bot actually do something with presence data, or am I just displaying it in a "user info" command?
2. Could I accomplish my goal with `approximate_presence_count` instead of accessing individual users' presence?
3. Am I requesting this intent "just in case" or because a core feature depends on it?
***
## Message Content Intent
### What you get with the intent
The Message Content intent controls access to several fields within message objects across both Gateway events and REST API responses:
* `content`: The text body of the message
* `embeds`: Embedded content
* `attachments`: Files attached to the message
* `components`: Message components (buttons, selects, etc.) sent by users
* `poll`: Poll data
Without this intent, these fields will be empty strings or empty arrays when your app receives messages, with a few important exceptions.
### Exceptions: when you get message content without the privileged intent
The Message Content intent is **not** needed for your app to access message content in these situations:
* **Messages your app sends**
* **Direct Messages sent to your app**
* **Messages that @mention your app**
* **Replies to your app's messages.** Note: this applies to replies sent using Discord's reply feature to a regular bot message (not an interaction response) and the user has "ping on reply" enabled. It does not apply to replies to slash command responses.
If your app's functionality can be built to work with this access, you should not request the Message Content intent.
### What you can do without the intent
We have built a robust set of interaction primitives specifically so that most bots don't need access to the Message Content intent:
**[Slash Commands](developers/interactions/application-commands)**
The most direct replacement for text commands. Users type `/command` and Discord handles argument parsing, validation, and autocomplete for you. Slash commands accept `string`, `integer`, `number`, `boolean`, `user`, `channel`, `role`, `mentionable`, and `attachment` option types, covering the vast majority of input scenarios.
**[Message Context Menu Commands](/developers/interactions/application-commands#message-commands)**
These appear when a user right-clicks (or long-presses) a message. Your bot receives the full message object, including its content. This is ideal for "act on this message" features like reporting, translating, pinning, or saving, without needing to read every message in a channel.
**[Message Components](/developers/components/overview)**
Buttons and select menus let you gather structured user input. Perfect for polls, verification systems, role selection, confirmations, and multi-step workflows.
**[Modals with Text Inputs](/developers/components/using-modal-components)**
Pop-up forms that collect freeform text, checkboxes, and other structured input from users. Great for forms, applications, feedback collection, or any scenario where you need the user to type a specific response.
### Decision checklist
1. Is my bot using prefix commands (`!help`, `?play`) that could be migrated to slash commands?
2. Could my feature work if users explicitly invoked or interacted with it (ie: via a command or context menu)?
3. Does my bot need to read the content of messages, or does it just need to know that a message was sent?
4. Is my bot's intended use case for moderation, and if so, does it provide functionality beyond what is possible with the AutoMod API? Providing what the AutoMod API already supports is generally not considered a compelling use case for access.
## Further Reading
* [Guide: Getting Started with Privileged Intent Review](/developers/gateway/getting-started-with-privileged-intent-review)
# Discord Development Guide Library
Source: https://docs.discord.com/developers/guides
Guides to help you get started on your development journey with Discord.
## Building on Discord
Guides for developing bots on Discord.
Guides for developing embedded games and experiences on Discord.
Guides for integrating games with the Discord Social SDK.
Guides for using Discord's features across your game, app, and server experience.
## Community & Servers
Guides for managing your Discord community and server effectively.
## Building Games
Guides for building games and game development communities on Discord.
# Discord Activity Development Guides
Source: https://docs.discord.com/developers/guides/activities
Guides for developing embedded games and experiences on Discord.
Visit [Activities](/developers/activities/overview) for more on building embedded games and experiences on Discord, including how to get started, best practices, and more.
## Getting Started
A step-by-step guide to building your first Activity on Discord.
## Diving Deeper
Learn how to set up Rich Presence for your Activity to display game status and join features.
A guide to implementing in-app purchases for your Discord Activities.
## Best Practices
Best practices and design patterns for creating engaging Activities on Discord.
# Discord Bot Development Guides
Source: https://docs.discord.com/developers/guides/bots
Guides for developing bots on Discord.
Visit [Bots & Companion Apps](/developers/bots/overview) for more on building bots and companion apps on Discord, including how to get started, best practices, and more.
## Getting Started
A step-by-step guide to building your first bot on Discord.
## Diving Deeper
A guide to using message components in your Discord bot.
A guide to using modal components in your Discord bot.
}>
A guide to using community invites in your Discord bot.
}>
A guide to developing a user-installable app on Discord.
}>
A guide to hosting your Discord bot on Cloudflare Workers.
}>
A guide to help you determine whether you need a privileged intent or whether an alternative approach will work.
## Privileged Intents
}>
A guide to help you determine whether you need a privileged intent or whether an alternative approach will work.
}>
A guide to help you get started with the privileged intent review process.
# Community Management Guides
Source: https://docs.discord.com/developers/guides/communities
Guides for managing your Discord community and server effectively.
Creating a community for your game involves establishing a Discord server to foster direct communication with players, gather feedback, and keep engagement alive between updates.
}>
Learn how to create and manage community invites to grow your community.
# Game Development Guides
Source: https://docs.discord.com/developers/guides/game-development
Guides for creating games using Discord's APIs, SDKs, and communities.
## Strategy
Learn how to use Discord to grow your game, build a community, and keep players playing!
## Guides
Set up a community server, design roles and channels, and bring players in from your game.
Use Discord's features to get your game seen by more players and grow your community.
Implement Rich Presence with game details, lobby joining, game launching, and store links.
Build a bot connected to your game with leaderboards, event announcements, and achievement sharing.
Learn how to use the Discord Social SDK's voice chat with Unity's 3D audio system to build proximity voice chat for a multiplayer game.
# Platform Feature Guides
Source: https://docs.discord.com/developers/guides/platform
Guides for using Discord's features across your game, app, and server experience.
## Account Linking
An introduction to account linking and its benefits for your game or app.
## Rich Presence
Best practices for using Rich Presence to create engaging game integrations on Discord.
# Discord Social SDK Guides
Source: https://docs.discord.com/developers/guides/social-sdk
Development guides for integrating the Discord Social SDK into your game.
Visit [Discord Social SDK](/developers/discord-social-sdk/overview) for more on integrating games with the Discord Social SDK, including how to get started, best practices, and more.
## Getting Started
A guide to integrating your game with the Discord Social SDK.
## Guides
}>
Learn how to authenticate users with their Discord accounts using OAuth2.
}>
Combine Discord and game-specific friends in one view.
}>
Display detailed game status in Discord profiles.
}>
Allow players to invite friends to join their game session or party.
}>
Enable private messaging between players.
}>
Bring players together in a shared lobby with invites, text chat, and voice comms.
}>
Connect game lobbies to Discord text channels.
}>
Add in-game voice communication.
}>
Give your users a seamless account experience with provisional accounts.
## Game Development Guides
Implement Rich Presence with game details, lobby joining, game launching, and store links using the Social SDK
Learn how to use the Social SDK's voice chat with Unity's 3D audio system to build proximity voice chat for a multiplayer game.
# Application Commands
Source: https://docs.discord.com/developers/interactions/application-commands
Complete guide to creating slash commands, user commands, and message commands.
Application commands are native ways to interact with apps in the Discord client. There are 3 types of commands accessible in different interfaces: the chat input, a message's context menu (top-right menu or right-clicking in a message), and a user's context menu (right-clicking on a user).
## Application Command Object
###### Application Command Naming
`CHAT_INPUT` command names and command option names must match the following regex `^[-_'\p{L}\p{N}\p{sc=Deva}\p{sc=Thai}]{1,32}$` with the unicode flag set. If there is a lowercase variant of any letters used, you must use those. Characters with no lowercase variants and/or uncased letters are still allowed. `USER` and `MESSAGE` commands may be mixed case and can include spaces.
###### Application Command Structure
| Field | Type | Description | Valid Types |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- |
| id | snowflake | Unique ID of command | all |
| type? | one of [command types](/developers/interactions/application-commands#application-command-object-application-command-types) | [Type of command](/developers/interactions/application-commands#application-command-object-application-command-types), defaults to `1` | all |
| application\_id | snowflake | ID of the parent application | all |
| guild\_id? | snowflake | Guild ID of the command, if not global | all |
| name | string | [Name of command](/developers/interactions/application-commands#application-command-object-application-command-naming), 1-32 characters | all |
| name\_localizations? | ?dictionary with keys in [available locales](/developers/reference#locales) | Localization dictionary for `name` field. Values follow the same restrictions as `name` | all |
| description | string | Description for `CHAT_INPUT` commands, 1-100 characters. Empty string for `USER` and `MESSAGE` commands | all |
| description\_localizations? | ?dictionary with keys in [available locales](/developers/reference#locales) | Localization dictionary for `description` field. Values follow the same restrictions as `description` | all |
| options? \* | array of [command options](/developers/interactions/application-commands#application-command-object-application-command-option-structure) | Parameters for the command, max of 25 | CHAT\_INPUT |
| default\_member\_permissions | ?string | Set of [permissions](/developers/topics/permissions) represented as a bit set | all |
| dm\_permission? | boolean | **Deprecated (use `contexts` instead)**; Indicates whether the command is available in DMs with the app, only for globally-scoped commands. By default, commands are visible. | all |
| default\_permission? | ?boolean | Not recommended for use as field will soon be deprecated. Indicates whether the command is enabled by default when the app is added to a guild, defaults to `true` | all |
| nsfw? | boolean | Indicates whether the command is [age-restricted](/developers/interactions/application-commands#agerestricted-commands), defaults to `false` | all |
| integration\_types? | list of [integration types](/developers/resources/application#application-object-application-integration-types) | [Installation contexts](/developers/resources/application#installation-context) where the command is available, only for globally-scoped commands. Defaults to your app's [configured contexts](/developers/resources/application#setting-supported-installation-contexts) | all |
| contexts? | ?list of [interaction context types](/developers/interactions/receiving-and-responding#interaction-object-interaction-context-types) | [Interaction context(s)](/developers/interactions/receiving-and-responding#interaction-object-interaction-context-types) where the command can be used, only for globally-scoped commands. | all |
| version | snowflake | Autoincrementing version identifier updated during substantial record changes | all |
| handler? | one of [command handler types](/developers/interactions/application-commands#application-command-object-entry-point-command-handler-types) | Determines whether the interaction is handled by the app's interactions handler or by Discord | PRIMARY\_ENTRY\_POINT |
\* `options` can only be set for application commands of type `CHAT_INPUT`.
\* `handler` can only be set for application commands of type `PRIMARY_ENTRY_POINT` for applications with the `EMBEDDED` flag (i.e. applications that have an Activity).
`default_permission` will soon be deprecated. You can instead set `default_member_permissions` to `"0"` to disable the command for everyone except admins by default, and/or use `contexts` to disable globally-scoped commands inside of DMs with your app
###### Application Command Types
| Name | Type | Description |
| --------------------- | ---- | ----------------------------------------------------------------------------------------------------------------- |
| CHAT\_INPUT | 1 | Slash commands; a text-based command that shows up when a user types `/` |
| USER | 2 | A UI-based command that shows up when you right click or tap on a user |
| MESSAGE | 3 | A UI-based command that shows up when you right click or tap on a message |
| PRIMARY\_ENTRY\_POINT | 4 | A UI-based command that represents the primary way to invoke an app's [Activity](/developers/activities/overview) |
###### Application Command Option Structure
Required `options` must be listed before optional options
| Field | Type | Description | Valid Option Types |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- |
| type | one of [application command option type](/developers/interactions/application-commands#application-command-object-application-command-option-type) | Type of option | all |
| name \* | string | [1-32 character name](/developers/interactions/application-commands#application-command-object-application-command-naming) | all |
| name\_localizations? | ?dictionary with keys in [available locales](/developers/reference#locales) | Localization dictionary for the `name` field. Values follow the same restrictions as `name` | all |
| description | string | 1-100 character description | all |
| description\_localizations? | ?dictionary with keys in [available locales](/developers/reference#locales) | Localization dictionary for the `description` field. Values follow the same restrictions as `description` | all |
| required? | boolean | Whether the parameter is required or optional, default `false` | all but `SUB_COMMAND` and `SUB_COMMAND_GROUP` |
| choices? | array of [application command option choice](/developers/interactions/application-commands#application-command-object-application-command-option-choice-structure) | Choices for the user to pick from, max 25 | `STRING`, `INTEGER`, `NUMBER` |
| options? | array of [application command option](/developers/interactions/application-commands#application-command-object-application-command-option-structure) | If the option is a subcommand or subcommand group type, these nested options will be the parameters or subcommands respectively; up to 25 | `SUB_COMMAND` , `SUB_COMMAND_GROUP` |
| channel\_types? | array of [channel types](/developers/resources/channel#channel-object-channel-types) | The channels shown will be restricted to these types | `CHANNEL` |
| min\_value? | integer for `INTEGER` options, double for `NUMBER` options | The minimum value permitted | `INTEGER` , `NUMBER` |
| max\_value? | integer for `INTEGER` options, double for `NUMBER` options | The maximum value permitted | `INTEGER` , `NUMBER` |
| min\_length? | integer | The minimum allowed length (minimum of `0`, maximum of `6000`) | `STRING` |
| max\_length? | integer | The maximum allowed length (minimum of `1`, maximum of `6000`) | `STRING` |
| autocomplete? \*\* | boolean | If autocomplete interactions are enabled for this option | `STRING`, `INTEGER`, `NUMBER` |
\* `name` must be unique within an array of [application command options](/developers/interactions/application-commands#application-command-object-application-command-option-structure).
\*\* `autocomplete` may not be set to true if `choices` are present.
Options using `autocomplete` are not confined to only use choices given by the application.
###### Application Command Option Type
| Name | Value | Note |
| ------------------- | ----- | -------------------------------------------------------------------- |
| SUB\_COMMAND | 1 | |
| SUB\_COMMAND\_GROUP | 2 | |
| STRING | 3 | |
| INTEGER | 4 | Any integer between -2^53+1 and 2^53-1 |
| BOOLEAN | 5 | |
| USER | 6 | |
| CHANNEL | 7 | Includes all channel types + categories |
| ROLE | 8 | |
| MENTIONABLE | 9 | Includes users and roles |
| NUMBER | 10 | Any double between -2^53 and 2^53 |
| ATTACHMENT | 11 | [attachment](/developers/resources/message#attachment-object) object |
###### Application Command Option Choice Structure
If you specify `choices` for an option, they are the **only** valid values for a user to pick
| Field | Type | Description |
| -------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| name | string | 1-100 character choice name |
| name\_localizations? | ?dictionary with keys in [available locales](/developers/reference#locales) | Localization dictionary for the `name` field. Values follow the same restrictions as `name` |
| value | string, integer, or double \* | Value for the choice, up to 100 characters if string |
\* Type of `value` depends on the [option type](/developers/interactions/application-commands#application-command-object-application-command-option-type) that the choice belongs to.
###### Entry Point Command Handler Types
| Name | Value | Note |
| ------------------------- | ----- | -------------------------------------------------------------------------------------------------------------------------- |
| APP\_HANDLER | 1 | The app handles the interaction using an interaction token |
| DISCORD\_LAUNCH\_ACTIVITY | 2 | Discord handles the interaction by launching an Activity and sending a follow-up message without coordinating with the app |
Details about Entry Point command handler types are in the [Entry Point handlers](/developers/interactions/application-commands#entry-point-handlers) section.
## Authorizing Your Application
Application commands do not depend on a bot user in the guild; they use the [interactions](/developers/interactions/receiving-and-responding) model. To create commands in a guild, your app must be authorized with the `applications.commands` scope which can be used independently, but is also automatically included with the `bot` scope.
When requesting this scope, we "shortcut" the OAuth2 flow similar to adding a bot. You don't need to complete the flow, exchange for a token, or any of that.
If your application does not require a bot user in the guild for its commands to work, **you don't need to add the bot scope or a permission bitfield to the URL**.
## Registering a Command
Commands can only be registered via HTTP endpoint.
Commands can be scoped either globally or to a specific guild. Global commands are available for every guild that adds your app. An individual app's global commands are also available in DMs if that app has a bot that shares a mutual guild with the user.
Guild commands are specific to the guild you specify when making them. Guild commands are not available in DMs. Command names are unique per application, per type, within each scope (global and guild). That means:
* Your app **cannot** have two global `CHAT_INPUT` commands with the same name
* Your app **cannot** have two guild `CHAT_INPUT` commands within the same name **on the same guild**
* Your app **cannot** have two global `USER` commands with the same name
* Your app **can** have a global and guild `CHAT_INPUT` command with the same name
* Your app **can** have a global `CHAT_INPUT` and `USER` command with the same name
* Your app **cannot** have a `PRIMARY_ENTRY_POINT` guild command
* Multiple apps **can** have commands with the same names
This list is non-exhaustive. In general, remember that command names must be unique per application, per type, and within each scope (global and guild).
An app can have the following number of commands:
* 100 global `CHAT_INPUT` commands
* 15 global `USER` commands
* 15 global `MESSAGE` commands
* 1 global `PRIMARY_ENTRY_POINT` command
For all command types except `PRIMARY_ENTRY_POINT`, you can have the same amount of guild-specific commands per guild.
There is a global rate limit of 200 application command creates per day, per guild
### Making a Global Command
Global commands are available on *all* your app's guilds.
Global commands have inherent read-repair functionality. That means that if you make an update to a global command, and a user tries to use that command before it has updated for them, Discord will do an internal version check and reject the command, and trigger a reload for that command.
To make a **global** command, make an HTTP POST call like this:
```py theme={"system"}
import requests
url = "https://discord.com/api/v10/applications//commands"
# This is an example CHAT_INPUT or Slash Command, with a type of 1
json = {
"name": "blep",
"type": 1,
"description": "Send a random adorable animal photo",
"options": [
{
"name": "animal",
"description": "The type of animal",
"type": 3,
"required": True,
"choices": [
{
"name": "Dog",
"value": "animal_dog"
},
{
"name": "Cat",
"value": "animal_cat"
},
{
"name": "Penguin",
"value": "animal_penguin"
}
]
},
{
"name": "only_smol",
"description": "Whether to show only baby animals",
"type": 5,
"required": False
}
]
}
# For authorization, you can use either your bot token
headers = {
"Authorization": "Bot "
}
# or a client credentials token for your app with the applications.commands.update scope
headers = {
"Authorization": "Bearer "
}
r = requests.post(url, headers=headers, json=json)
```
### Making a Guild Command
Guild commands are available only within the guild specified on creation. Guild commands update **instantly**. We recommend you use guild commands for quick testing, and global commands when they're ready for public use.
To make a **guild** command, make a similar HTTP POST call, but scope it to a specific `guild_id`:
```py theme={"system"}
import requests
url = "https://discord.com/api/v10/applications//guilds//commands"
# This is an example USER command, with a type of 2
json = {
"name": "High Five",
"type": 2
}
# For authorization, you can use either your bot token
headers = {
"Authorization": "Bot "
}
# or a client credentials token for your app with the applications.commands.update scope
headers = {
"Authorization": "Bearer "
}
r = requests.post(url, headers=headers, json=json)
```
## Updating and Deleting a Command
Commands can be deleted and updated by making `DELETE` and `PATCH` calls to the command endpoint. Those endpoints are
* `applications//commands/` for global commands, or
* `applications//guilds//commands/` for guild commands
Because commands have unique names within a type and scope, we treat `POST` requests for new commands as upserts. That means **making a new command with an already-used name for your application will update the existing command**.
Detailed documentation about application command endpoints and their parameters are [in the endpoints section](/developers/interactions/application-commands#endpoints).
## Contexts
Commands have two sets of contexts on the [application command object](/developers/interactions/application-commands#application-command-object) that let you to configure when and where it can be used:
* `integration_types` defines the **[installation contexts](/developers/interactions/application-commands#installation-context)** that a command supports
* `contexts` defines the **[interaction contexts](/developers/interactions/application-commands#interaction-contexts)** where a command can be used
Details for both types of command contexts are in the sections below.
Contexts are distinct from, and do not affect, any [command permissions](/developers/interactions/application-commands#permissions) for apps installed to a server.
### Installation Context
The [installation context](/developers/resources/application#installation-context) is where your app was installed—to a server, a user, or both. If your app supports both installation contexts, there may be cases where you want some of your app's commands to only be available for one or the other. For example, maybe your app has a `/profile` command that is only relevant when it's installed to a user.
A command's supported installation context(s) can be set using the [`integration_types` field](/developers/interactions/application-commands#application-command-object-application-command-structure) when creating or updating a command as long as any included contexts are already [supported on the application-level](/developers/resources/application#setting-supported-installation-contexts).
A command's value for `integration_types` may affect which [interaction contexts](/developers/interactions/application-commands#interaction-contexts) a command is visible in.
### Interaction Contexts
The interaction contexts for a command determines where in the Discord client it can be used, and can be configured by setting the [`contexts` field](/developers/interactions/application-commands#application-command-object-application-command-structure) when creating or updating a command.
There are three [interaction context types](/developers/interactions/receiving-and-responding#interaction-object-interaction-context-types) that correspond to different surfaces: `GUILD` (`0`), `BOT_DM` (`1`), and `PRIVATE_CHANNEL` (`2`). However, the `PRIVATE_CHANNEL` interaction context is only meaningful for commands installed to a user (when the command's `integration_types` includes `USER_INSTALL`).
## Permissions
Application command permissions allow your app to enable or disable commands for up to 100 users, roles, and channels within a guild. Command permissions can also be updated by users in the client if they have the necessary permissions.
Command permissions can only be updated using a [Bearer token](/developers/topics/oauth2#client-credentials-grant). Authenticating with a bot token will result in an error.
A command's current permissions can be retrieved using the [`GET /applications/{application.id}/guilds/{guild.id}/commands/{command.id}/permissions`](/developers/interactions/application-commands#get-application-command-permissions) endpoint. The response will include an array called `permissions` with associated IDs and permission types.
Command permissions can be updated with the [`PUT /applications/{application.id}/guilds/{guild.id}/commands/{command.id}/permissions`](/developers/interactions/application-commands#edit-application-command-permissions) endpoint. To call the endpoint, apps must use a Bearer token that's authorized with the [`applications.commands.permissions.update`](/developers/topics/oauth2#shared-resources-oauth2-scopes) scope from a user with sufficient permissions. For their permissions to be considered sufficient, all of the following must be true for **the authenticating user** (not your app or bot user):
* Has [permission to Manage Guild and Manage Roles](/developers/topics/permissions) in the guild where the command is being edited
* Has the ability to run the command being edited
* Has permission to manage the resources that will be affected (roles, users, and/or channels depending on the [permission types](/developers/interactions/application-commands#application-command-permissions-object-application-command-permission-type))
### Syncing and Unsyncing Permissions
The command permissions interface can be accessed in the client by navigating to `Server Settings` > `Integrations`, then clicking `Manage` to the right of an installed app. At the top of the interface, users can edit permissions for a specific user, role, or channel. By default, these top-level permissions will apply to all of an app's commands. However, each permission can also be unsynced and customized for individual commands to provide more granular control.
When the permissions for a specific command are unsynced, meaning it doesn't align with the top-level permissions, the interface will display "Not Synced" to users.
### Application Command Permissions Object
###### Guild Application Command Permissions Structure
Returned when fetching the permissions for an app's command(s) in a guild.
| Field | Type | Description |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
| id | snowflake | ID of the command or the application ID |
| application\_id | snowflake | ID of the application the command belongs to |
| guild\_id | snowflake | ID of the guild |
| permissions | array of [application command permissions](/developers/interactions/application-commands#application-command-permissions-object-application-command-permissions-structure) | Permissions for the command in the guild, max of 100 |
When the `id` field is the application ID instead of a command ID, the permissions apply to all commands that do not contain explicit overwrites.
###### Application Command Permissions Structure
Application command permissions allow you to enable or disable commands for specific users, roles, or channels within a guild.
| Field | Type | Description |
| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id | snowflake | ID of the role, user, or channel. It can also be a [permission constant](/developers/interactions/application-commands#application-command-permissions-object-application-command-permissions-constants) |
| type | [application command permission type](/developers/interactions/application-commands#application-command-permissions-object-application-command-permission-type) | role (`1`), user (`2`), or channel (`3`) |
| permission | boolean | `true` to allow, `false`, to disallow |
###### Application Command Permissions Constants
The following constants can be used in the `id` field for command permissions payloads.
| Permission | Value | Type | Description |
| ------------ | -------------- | --------- | ----------------------- |
| `@everyone` | `guild_id` | snowflake | All members in a guild |
| All Channels | `guild_id - 1` | snowflake | All channels in a guild |
###### Application Command Permission Type
| Name | Value |
| ------- | ----- |
| ROLE | 1 |
| USER | 2 |
| CHANNEL | 3 |
To allow for fine-tuned access to commands, application command permissions are supported for guild and global commands of all types. Guild members and apps with the [necessary permissions](/developers/interactions/application-commands#permissions) can allow or deny specific users and roles from using a command, or toggle commands for entire channels.
Similar to how threads [inherit user and role permissions from the parent channel](/developers/topics/threads#permissions), any command permissions for a channel will apply to the threads it contains.
If you don't have permission to use a command, it will not show up in the command picker. Members with the Administrator permission can use all commands.
###### Using Default Permissions
Default permissions can be added to a command during creation using the `default_member_permissions` and `context` fields. Adding default permissions doesn't require any Bearer token since it's configured during command creation and isn't targeting specific roles, users, or channels.
The `default_member_permissions` field can be used when creating a command to set the permissions a user must have to use it. The value for `default_member_permissions` is a bitwise OR-ed set of [permissions](/developers/topics/permissions#permissions-bitwise-permission-flags), serialized as a string. Setting it to `"0"` will prohibit anyone in a guild from using the command unless a specific overwrite is configured or the user has admin permissions.
You can also include `BOT_DM` (`1`) in `contexts` when setting a global command's [interaction contexts](/developers/interactions/application-commands#interaction-contexts) to control whether it can be run in DMs with your app. Guild commands don't support the `BOT_DM` interaction context.
###### Example of editing permissions
As an example, the following command would not be usable by anyone except admins in any guilds by default:
```json theme={"system"}
{
"name": "permissions_test",
"description": "A test of default permissions",
"type": 1,
"default_member_permissions": "0"
}
```
Or this would enable it just for users that have the `MANAGE_GUILD` permission:
```py theme={"system"}
permissions = str(1 << 5)
command = {
"name": "permissions_test",
"description": "A test of default permissions",
"type": 1,
"default_member_permissions": permissions
}
```
And the following would disable a command for a specific channel:
```py theme={"system"}
A_SPECIFIC_CHANNEL = ""
url = "https://discord.com/api/v10/applications//guilds//commands//permissions"
json = {
"permissions": [
{
"id": A_SPECIFIC_CHANNEL,
"type": 3,
"permission": False
}
]
}
headers = {
"Authorization": "Bearer "
}
r = requests.put(url, headers=headers, json=json)
```
## Slash Commands
Slash commands—the `CHAT_INPUT` type—are a type of application command. They're made up of a name, description, and a block of `options`, which you can think of like arguments to a function. The name and description help users find your command among many others, and the `options` validate user input as they fill out your command.
Slash commands can also have groups and subcommands to further organize commands. More on those later.
Slash commands can have a maximum of 8000 characters for combined name, description, and value properties for each command, its options (including subcommands and groups), and choices. When [localization fields](/developers/interactions/application-commands#localization) are present, only the longest localization for each field (including the default value) is counted towards the size limit.
###### Example Slash Command
```json theme={"system"}
{
"name": "blep",
"type": 1,
"description": "Send a random adorable animal photo",
"options": [
{
"name": "animal",
"description": "The type of animal",
"type": 3,
"required": true,
"choices": [
{
"name": "Dog",
"value": "animal_dog"
},
{
"name": "Cat",
"value": "animal_cat"
},
{
"name": "Penguin",
"value": "animal_penguin"
}
]
},
{
"name": "only_smol",
"description": "Whether to show only baby animals",
"type": 5,
"required": false
}
]
}
```
When someone uses a slash command, your application will receive an interaction:
###### Example Interaction
```json theme={"system"}
{
"type": 2,
"token": "A_UNIQUE_TOKEN",
"member": {
"user": {
"id": "53908232506183680",
"username": "Mason",
"avatar": "a_d5efa99b3eeaa7dd43acca82f5692432",
"discriminator": "1337",
"public_flags": 131141
},
"roles": ["539082325061836999"],
"premium_since": null,
"permissions": "2147483647",
"pending": false,
"nick": null,
"mute": false,
"joined_at": "2017-03-13T19:19:14.040000+00:00",
"is_pending": false,
"deaf": false
},
"id": "786008729715212338",
"guild_id": "290926798626357999",
"app_permissions": "442368",
"guild_locale": "en-US",
"locale": "en-US",
"data": {
"options": [{
"type": 3,
"name": "cardname",
"value": "The Gitrog Monster"
}],
"type": 1,
"name": "cardsearch",
"id": "771825006014889984"
},
"channel_id": "645027906669510667"
}
```
## Subcommands and Subcommand Groups
Currently, subcommands and subcommand groups all appear at the top level in the command explorer. This may change in the future to include them as nested autocomplete options.
For those developers looking to make more organized and complex groups of commands, look no further than subcommands and groups.
**Subcommands** organize your commands by **specifying actions within a command or group**.
**Subcommand Groups** organize your **subcommands** by **grouping subcommands by similar action or resource within a command**.
These are not enforced rules. You are free to use subcommands and groups however you'd like; it's just how we think about them.
Using subcommands or subcommand groups will make your base command unusable. You can't send the base `/permissions` command as a valid command if you also have `/permissions add | remove` as subcommands or subcommand groups
We support nesting one level deep within a group, meaning your top level command can contain subcommand groups, and those groups can contain subcommands. **That is the only kind of nesting supported.** Here's some visual examples:
```
VALID
command
|
|__ subcommand
|
|__ subcommand
----
VALID
command
|
|__ subcommand-group
|
|__ subcommand
|
|__ subcommand-group
|
|__ subcommand
----
VALID
command
|
|__ subcommand-group
|
|__ subcommand
|
|__ subcommand
-------
INVALID
command
|
|__ subcommand-group
|
|__ subcommand-group
|
|__ subcommand-group
|
|__ subcommand-group
----
INVALID
command
|
|__ subcommand
|
|__ subcommand-group
|
|__ subcommand
|
|__ subcommand-group
```
### Example Walkthrough
Let's look at an example. Let's imagine you run a moderation bot. You want to make a `/permissions` command that can do the following:
* Get the guild permissions for a user or a role
* Get the permissions for a user or a role on a specific channel
* Change the guild permissions for a user or a role
* Change the permissions for a user or a role on a specific channel
We'll start by defining the top-level information for `/permissions`:
```js theme={"system"}
{
"name": "permissions",
"description": "Get or edit permissions for a user or a role",
"options": []
}
```
Now we have a command named `permissions`. We want this command to be able to affect users and roles. Rather than making two separate commands, we can use subcommand groups. We want to use subcommand groups here because we are grouping commands on a similar resource: `user` or `role`.
```js theme={"system"}
{
"name": "permissions",
"description": "Get or edit permissions for a user or a role",
"options": [
{
"name": "user",
"description": "Get or edit permissions for a user",
"type": 2 // 2 is type SUB_COMMAND_GROUP
},
{
"name": "role",
"description": "Get or edit permissions for a role",
"type": 2
}
]
}
```
You'll notice that a command like this **will not show up** in the command explorer. That's because groups are effectively "folders" for commands, and we've made two empty folders. So let's continue.
Now that we've effectively made `user` and `role` "folders", we want to be able to either `get` and `edit` permissions. Within the subcommand groups, we can make subcommands for `get` and `edit`:
```js theme={"system"}
{
"name": "permissions",
"description": "Get or edit permissions for a user or a role",
"options": [
{
"name": "user",
"description": "Get or edit permissions for a user",
"type": 2, // 2 is type SUB_COMMAND_GROUP
"options": [
{
"name": "get",
"description": "Get permissions for a user",
"type": 1 // 1 is type SUB_COMMAND
},
{
"name": "edit",
"description": "Edit permissions for a user",
"type": 1
}
]
},
{
"name": "role",
"description": "Get or edit permissions for a role",
"type": 2,
"options": [
{
"name": "get",
"description": "Get permissions for a role",
"type": 1
},
{
"name": "edit",
"description": "Edit permissions for a role",
"type": 1
}
]
}
]
}
```
Now, we need some arguments! If we chose `user`, we need to be able to pick a user; if we chose `role`, we need to be able to pick a role. We also want to be able to pick between guild-level permissions and channel-specific permissions. For that, we can use optional arguments:
```js theme={"system"}
{
"name": "permissions",
"description": "Get or edit permissions for a user or a role",
"options": [
{
"name": "user",
"description": "Get or edit permissions for a user",
"type": 2, // 2 is type SUB_COMMAND_GROUP
"options": [
{
"name": "get",
"description": "Get permissions for a user",
"type": 1, // 1 is type SUB_COMMAND
"options": [
{
"name": "user",
"description": "The user to get",
"type": 6, // 6 is type USER
"required": true
},
{
"name": "channel",
"description": "The channel permissions to get. If omitted, the guild permissions will be returned",
"type": 7, // 7 is type CHANNEL
"required": false
}
]
},
{
"name": "edit",
"description": "Edit permissions for a user",
"type": 1,
"options": [
{
"name": "user",
"description": "The user to edit",
"type": 6,
"required": true
},
{
"name": "channel",
"description": "The channel permissions to edit. If omitted, the guild permissions will be edited",
"type": 7,
"required": false
}
]
}
]
},
{
"name": "role",
"description": "Get or edit permissions for a role",
"type": 2,
"options": [
{
"name": "get",
"description": "Get permissions for a role",
"type": 1,
"options": [
{
"name": "role",
"description": "The role to get",
"type": 8, // 8 is type ROLE
"required": true
},
{
"name": "channel",
"description": "The channel permissions to get. If omitted, the guild permissions will be returned",
"type": 7,
"required": false
}
]
},
{
"name": "edit",
"description": "Edit permissions for a role",
"type": 1,
"options": [
{
"name": "role",
"description": "The role to edit",
"type": 8,
"required": true
},
{
"name": "channel",
"description": "The channel permissions to edit. If omitted, the guild permissions will be edited",
"type": 7,
"required": false
}
]
}
]
}
]
}
```
And, done! The JSON looks a bit complicated, but what we've ended up with is a single command that can be scoped to multiple actions, and then further scoped to a particular resource, and then even *further* scope with optional arguments. Here's what it looks like all put together.
## User Commands
User commands are application commands that appear on the context menu (right click or tap) of users. They're a great way to surface quick actions for your app that target users. They don't take any arguments, and will return the user on whom you clicked or tapped in the interaction response.
A user must have permission to send text messages in the channel they invoke a user command in. If they don't have this permission, they will receive a 'Permission Denied' error from the interaction.
The `description` field is not allowed when creating user commands. However, to avoid breaking changes to data models, `description` will be an **empty string** (instead of `null`) when fetching commands.
###### Example User Command
```json theme={"system"}
{
"name": "View Stats",
"type": 2
}
```
When someone uses a user command, your application will receive an interaction:
###### Example Interaction
```json theme={"system"}
{
"application_id": "775799577604522054",
"channel_id": "772908445358620702",
"data": {
"id": "866818195033292850",
"name": "context-menu-user-2",
"resolved": {
"members": {
"809850198683418695": {
"avatar": null,
"is_pending": false,
"joined_at": "2021-02-12T18:25:07.972000+00:00",
"nick": null,
"pending": false,
"permissions": "246997699136",
"premium_since": null,
"roles": []
}
},
"users": {
"809850198683418695": {
"avatar": "afc428077119df8aabbbd84b0dc90c74",
"bot": true,
"discriminator": "7302",
"id": "809850198683418695",
"public_flags": 0,
"username": "VoltyDemo"
}
}
},
"target_id": "809850198683418695",
"type": 2
},
"guild_id": "772904309264089089",
"guild_locale": "en-US",
"app_permissions": "442368",
"id": "867794291820986368",
"locale": "en-US",
"member": {
"avatar": null,
"deaf": false,
"is_pending": false,
"joined_at": "2020-11-02T20:46:57.364000+00:00",
"mute": false,
"nick": null,
"pending": false,
"permissions": "274877906943",
"premium_since": null,
"roles": ["785609923542777878"],
"user": {
"avatar": "a_f03401914fb4f3caa9037578ab980920",
"discriminator": "6538",
"id": "167348773423415296",
"public_flags": 1,
"username": "ian"
}
},
"token": "UNIQUE_TOKEN",
"type": 2,
"version": 1
}
```
## Message Commands
Message commands are application commands that appear on the context menu (right click or tap) of messages. They're a great way to surface quick actions for your app that target messages. They don't take any arguments, and will return the message on whom you clicked or tapped in the interaction response.
The `description` field is not allowed when creating message commands. However, to avoid breaking changes to data models, `description` will be an **empty string** (instead of `null`) when fetching commands.
###### Example Message Command
```json theme={"system"}
{
"name": "Challenge",
"type": 3
}
```
When someone uses a message command, your application will receive an interaction:
###### Example Interaction
```json theme={"system"}
{
"application_id": "775799577604522054",
"channel_id": "772908445358620702",
"data": {
"id": "866818195033292851",
"name": "context-menu-message-2",
"resolved": {
"messages": {
"867793854505943041": {
"attachments": [],
"author": {
"avatar": "a_f03401914fb4f3caa9037578ab980920",
"discriminator": "6538",
"id": "167348773423415296",
"public_flags": 1,
"username": "ian"
},
"channel_id": "772908445358620702",
"components": [],
"content": "some message",
"edited_timestamp": null,
"embeds": [],
"flags": 0,
"id": "867793854505943041",
"mention_everyone": false,
"mention_roles": [],
"mentions": [],
"pinned": false,
"timestamp": "2021-07-22T15:42:57.744000+00:00",
"tts": false,
"type": 0
}
}
},
"target_id": "867793854505943041",
"type": 3
},
"guild_id": "772904309264089089",
"guild_locale": "en-US",
"app_permissions": "442368",
"id": "867793873336926249",
"locale": "en-US",
"member": {
"avatar": null,
"deaf": false,
"is_pending": false,
"joined_at": "2020-11-02T20:46:57.364000+00:00",
"mute": false,
"nick": null,
"pending": false,
"permissions": "274877906943",
"premium_since": null,
"roles": ["785609923542777878"],
"user": {
"avatar": "a_f03401914fb4f3caa9037578ab980920",
"discriminator": "6538",
"id": "167348773423415296",
"public_flags": 1,
"username": "ian"
}
},
"token": "UNIQUE_TOKEN",
"type": 2,
"version": 1
}
```
## Entry Point Commands
An Entry Point command serves as the primary way for users to open an app's [Activity](/developers/activities/overview) from the [App Launcher](https://support.discord.com/hc/articles/21334461140375-Using-Apps-on-Discord#h_01HRQSA6C8TRHS722P1H3HW1TV).
For the Entry Point command to be visible to users, an app must have [Activities](/developers/activities/overview) enabled.
###### Example Entry Point Command
```json theme={"system"}
{
"name": "launch",
"description": "Launch Racing with Friends",
"type": 4,
"handler": 2
}
```
### Entry Point handlers
When a user invokes an app's Entry Point command, the value of [`handler`](/developers/interactions/application-commands#application-command-object-application-command-structure) will determine how the interaction is handled:
* For `APP_HANDLER` (`1`), the app is responsible for [responding to the interaction](/developers/interactions/receiving-and-responding#responding-to-an-interaction). It can respond by launching the app's associated Activity using the `LAUNCH_ACTIVITY` (type `12`) [interaction callback type](/developers/interactions/receiving-and-responding#interaction-response-object-interaction-callback-type), or take another action (like sending a follow-up message in channel).
* For `DISCORD_LAUNCH_ACTIVITY` (`2`), Discord will handle the interaction automatically by launching the associated Activity and sending a message to the channel where it was launched.
### Default Entry Point command
When you enable Activities, an Entry Point command (named "Launch") is automatically created for your app with `DISCORD_LAUNCH_ACTIVITY` (`2`) set as the [Entry Point handler](/developers/interactions/application-commands#entry-point-handlers). You can retrieve details for the automatically-created command, like its ID, by calling the [Get Global Application Commands](/developers/interactions/application-commands##get-global-application-commands) endpoint and looking for the "Launch" command.
Details about updating or replacing the default Entry Point command is in the [Setting Up an Entry Point Command guide](/developers/activities/development-guides/user-actions#setting-up-an-entry-point-command).
## Autocomplete
Autocomplete interactions allow your application to dynamically return option suggestions to a user as they type.
An autocomplete interaction **can return partial data** for option values. Your application will receive partial data for any existing user input, as long as that input passes client-side validation. For example, you may receive partial strings, but not invalid numbers. The option the user is currently typing will be sent with a `focused: true` boolean field and options the user has already filled will also be sent but without the `focused` field. This is a special case where options that are otherwise required might not be present, due to the user not having filled them yet.
This validation is **client-side only**.
```json theme={"system"}
{
"type": 4,
"data": {
"id": "816437322781949972",
"name": "airhorn",
"type": 1,
"version": "847194950382780532",
"options": [
{
"type": 3,
"name": "variant",
"value": "data a user is typ",
"focused": true
}
]
}
}
```
## Localization
Application commands can be localized, which will cause them to use localized names and descriptions depending on the client's selected language. This is entirely optional. Localization is available for names and descriptions of commands, subcommands, and options, as well as the names of choices, by submitting the appropriate `name_localizations` and `description_localizations` fields when creating or updating the application command.
Application commands may be partially localized - not all [available locales](/developers/reference#locales) are required, nor do different fields within a command need to support the same set of locales. If a locale is not present in a localizations dictionary for a field, users in that locale will see the default value for that field. It's not necessary to fill out all locales with the default value. Any localized values that are identical to the default will be ignored.
Localized option names are subject to an additional constraint, which is that they must be distinct from all other default option names of that command, as well as all other option names within that locale on that command.
When taking advantage of command localization, the interaction payload received by your client will still use default command, subcommand, and option names. To localize your interaction response, you can determine the client's selected language by using the `locale` key in the interaction payload.
An application command furnished with localizations might look like this:
```json theme={"system"}
{
"name": "birthday",
"type": 1,
"description": "Wish a friend a happy birthday",
"name_localizations": {
"zh-CN": "生日",
"el": "γενέθλια"
},
"description_localizations": {
"zh-CN": "祝你朋友生日快乐"
},
"options": [
{
"name": "age",
"type": 4,
"description": "Your friend's age",
"name_localizations": {
"zh-CN": "岁数"
},
"description_localizations": {
"zh-CN": "你朋友的岁数"
}
}
]
}
```
### Locale fallbacks
For application commands, there are built-in fallbacks in case a user's locale isn't present in the localizations. If the fallback locale is also missing, it will use the default.
You should make sure to include your default value in its proper locale key, otherwise it may use a fallback value unexpectedly. For example, if your default value is `en-US`, but you don't specify the `en-US` value in your localizations, users with `en-US` selected will see the `en-GB` value if it's specified. For example, if you have a command with the default name "color", and your localizations specify only the `en-GB` value as "colour", users in the `en-US` locale will see "colour" because the `en-US` key is missing.
| Locale | Fallback |
| ------ | -------- |
| en-US | en-GB |
| en-GB | en-US |
| es-419 | es-ES |
### Retrieving localized commands
While most endpoints that return application command objects will return the `name_localizations` and `description_localizations` fields, some will not by default. This includes `GET` endpoints that return all of an application's guild or global commands. Instead, those endpoints will supply additional `name_localized` or `description_localized` fields, which only contain the localization relevant to the requester's locale. (The full dictionaries can still be obtained by supplying the appropriate query argument).
For example, if a batch `GET` request were made with locale `zh-CN`, including the above command, the returned object would look as follows:
```json theme={"system"}
{
"name": "birthday",
"type": 1,
"description": "Wish a friend a happy birthday",
"name_localized": "生日",
"description_localized": "祝你朋友生日快乐",
"options": [
{
"name": "age",
"type": 4,
"description": "Your friend's age",
"name_localized": "岁数",
"description_localized": "你朋友的岁数",
}
]
}
```
If the requester's locale is not found in a localizations dictionary, then the corresponding `name_localization` or `description_localization` for that field will also not be present.
Locale is determined by looking at the `X-Discord-Locale` header, then the `Accept-Language` header if not present, then lastly the user settings locale.
## Age-Restricted Commands
A command that contains age-restricted content should have the [`nsfw` field](/developers/interactions/application-commands#application-command-object-application-command-structure) set to `true` upon creation or update. Marking a command as age-restricted will limit who can see and access the command, and from which channels.
Apps with [discovery enabled](https://support-dev.discord.com/hc/en-us/articles/9489299950487) (which is required to appear in the App Directory) cannot contain any age-restricted commands or content.
### Using Age-Restricted Commands
To use an age-restricted command, a user must be 18 years or older and access the command from either:
* an [age-restricted channel](https://support.discord.com/hc/articles/115000084051-Age-Restricted-Channels-and-Content) or
* a DM with the app *after* [enabling age-restricted commands](https://support.discord.com/hc/en-us/articles/10123937946007) within their User Settings.
Details about accessing and using age-restricted commands is in [the Help Center](https://support.discord.com/hc/en-us/articles/10123937946007).
### Endpoints
For authorization, all endpoints take either a [bot token](/developers/reference#authentication) or [client credentials token](/developers/topics/oauth2#client-credentials-grant) for your application
## Get Global Application Commands
/applications/[\{application.id}](/developers/resources/application#application-object)/commands
The objects returned by this endpoint may be augmented with [additional fields if localization is active](/developers/interactions/application-commands#retrieving-localized-commands).
Fetch all of the global commands for your application. Returns an array of [application command](/developers/interactions/application-commands#application-command-object) objects.
###### Query String Params
| Field | Type | Description |
| -------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| with\_localizations? | [boolean](/developers/reference#boolean-query-strings) | Whether to include full localization dictionaries (`name_localizations` and `description_localizations`) in the returned objects, instead of the `name_localized` and `description_localized` fields. Default `false`. |
## Create Global Application Command
/applications/[\{application.id}](/developers/resources/application#application-object)/commands
Creating a command with the same name as an existing command for your application will overwrite the old command.
Create a new global command. Returns `201` if a command with the same name does not already exist, or a `200` if it does (in which case the previous command will be overwritten). Both responses include an [application command](/developers/interactions/application-commands#application-command-object) object.
###### JSON Params
| Field | Type | Description |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name | string | [Name of command](/developers/interactions/application-commands#application-command-object-application-command-naming), 1-32 characters |
| name\_localizations? | ?dictionary with keys in [available locales](/developers/reference#locales) | Localization dictionary for the `name` field. Values follow the same restrictions as `name` |
| description? | string | 1-100 character description for `CHAT_INPUT` commands |
| description\_localizations? | ?dictionary with keys in [available locales](/developers/reference#locales) | Localization dictionary for the `description` field. Values follow the same restrictions as `description` |
| options? | array of [application command option](/developers/interactions/application-commands#application-command-object-application-command-option-structure) | Parameters for the command, max of 25. Only for `CHAT_INPUT` commands |
| default\_member\_permissions? | ?string | Set of [permissions](/developers/topics/permissions) represented as a bit set |
| dm\_permission? | ?boolean | **Deprecated (use `contexts` instead)**; Indicates whether the command is available in DMs with the app, only for globally-scoped commands. By default, commands are visible. |
| default\_permission? | boolean | Replaced by `default_member_permissions` and will be deprecated in the future. Indicates whether the command is enabled by default when the app is added to a guild. Defaults to `true` |
| integration\_types? | list of [integration types](/developers/resources/application#application-object-application-integration-types) | [Installation context(s)](/developers/resources/application#installation-context) where the command is available |
| contexts? | list of [interaction context types](/developers/interactions/receiving-and-responding#interaction-object-interaction-context-types) | [Interaction context(s)](/developers/interactions/receiving-and-responding#interaction-object-interaction-context-types) where the command can be used |
| type? | one of [application command type](/developers/interactions/application-commands#application-command-object-application-command-types) | Type of command, defaults `1` if not set |
| nsfw? | boolean | Indicates whether the command is [age-restricted](/developers/interactions/application-commands#agerestricted-commands) |
| handler? | one of [entry point command handler types](/developers/interactions/application-commands#application-command-object-entry-point-command-handler-types) | Determines whether the interaction is handled by the app's interactions handler or by Discord. Only for `PRIMARY_ENTRY_POINT` commands |
## Get Global Application Command
/applications/[\{application.id}](/developers/resources/application#application-object)/commands/[\{command.id}](/developers/interactions/application-commands#application-command-object)
Fetch a global command for your application. Returns an [application command](/developers/interactions/application-commands#application-command-object) object.
## Edit Global Application Command
/applications/[\{application.id}](/developers/resources/application#application-object)/commands/[\{command.id}](/developers/interactions/application-commands#application-command-object)
All parameters for this endpoint are optional.
Edit a global command. Returns `200` and an [application command](/developers/interactions/application-commands#application-command-object) object. All fields are optional, but any fields provided will entirely overwrite the existing values of those fields.
###### JSON Params
| Field | Type | Description |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name? | string | [Name of command](/developers/interactions/application-commands#application-command-object-application-command-naming), 1-32 characters |
| name\_localizations? | ?dictionary with keys in [available locales](/developers/reference#locales) | Localization dictionary for the `name` field. Values follow the same restrictions as `name` |
| description? | string | 1-100 character description |
| description\_localizations? | ?dictionary with keys in [available locales](/developers/reference#locales) | Localization dictionary for the `description` field. Values follow the same restrictions as `description` |
| options? | array of [application command option](/developers/interactions/application-commands#application-command-object-application-command-option-structure) | Parameters for the command, max of 25. Only for `CHAT_INPUT` commands |
| default\_member\_permissions? | ?string | Set of [permissions](/developers/topics/permissions) represented as a bit set |
| dm\_permission? | ?boolean | **Deprecated (use `contexts` instead)**; Indicates whether the command is available in DMs with the app, only for globally-scoped commands. By default, commands are visible. |
| default\_permission? | boolean | Replaced by `default_member_permissions` and will be deprecated in the future. Indicates whether the command is enabled by default when the app is added to a guild. Defaults to `true` |
| integration\_types? | list of [integration types](/developers/resources/application#application-object-application-integration-types) | [Installation context(s)](/developers/resources/application#installation-context) where the command is available |
| contexts? | list of [interaction context types](/developers/interactions/receiving-and-responding#interaction-object-interaction-context-types) | [Interaction context(s)](/developers/interactions/receiving-and-responding#interaction-object-interaction-context-types) where the command can be used |
| nsfw? | boolean | Indicates whether the command is [age-restricted](/developers/interactions/application-commands#agerestricted-commands) |
| handler? | one of [entry point command handler types](/developers/interactions/application-commands#application-command-object-entry-point-command-handler-types) | Determines whether the interaction is handled by the app's interactions handler or by Discord. Only for `PRIMARY_ENTRY_POINT` commands |
## Delete Global Application Command
/applications/[\{application.id}](/developers/resources/application#application-object)/commands/[\{command.id}](/developers/interactions/application-commands#application-command-object)
Deletes a global command. Returns `204 No Content` on success.
## Bulk Overwrite Global Application Commands
/applications/[\{application.id}](/developers/resources/application#application-object)/commands
Takes a list of application commands, overwriting the existing global command list for this application. Returns `200` and a list of [application command](/developers/interactions/application-commands#application-command-object) objects. Commands that do not already exist will count toward daily application command create limits.
This will overwrite **all** types of application commands: slash commands, user commands, and message commands.
## Get Guild Application Commands
/applications/[\{application.id}](/developers/resources/application#application-object)/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/commands
The objects returned by this endpoint may be augmented with [additional fields if localization is active](/developers/interactions/application-commands#retrieving-localized-commands).
Fetch all of the guild commands for your application for a specific guild. Returns an array of [application command](/developers/interactions/application-commands#application-command-object) objects.
###### Query String Params
| Field | Type | Description |
| -------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| with\_localizations? | [boolean](/developers/reference#boolean-query-strings) | Whether to include full localization dictionaries (`name_localizations` and `description_localizations`) in the returned objects, instead of the `name_localized` and `description_localized` fields. Default `false`. |
## Create Guild Application Command
/applications/[\{application.id}](/developers/resources/application#application-object)/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/commands
Creating a command with the same name as an existing command for your application will overwrite the old command.
Create a new guild command. New guild commands will be available in the guild immediately. Returns `201` if a command with the same name does not already exist, or a `200` if it does (in which case the previous command will be overwritten). Both responses include an [application command](/developers/interactions/application-commands#application-command-object) object.
###### JSON Params
| Field | Type | Description |
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name | string | [Name of command](/developers/interactions/application-commands#application-command-object-application-command-naming), 1-32 characters |
| name\_localizations? | ?dictionary with keys in [available locales](/developers/reference#locales) | Localization dictionary for the `name` field. Values follow the same restrictions as `name` |
| description? | string | 1-100 character description for `CHAT_INPUT` commands |
| description\_localizations? | ?dictionary with keys in [available locales](/developers/reference#locales) | Localization dictionary for the `description` field. Values follow the same restrictions as `description` |
| options? | array of [application command option](/developers/interactions/application-commands#application-command-object-application-command-option-structure) | Parameters for the command, max of 25. Only for `CHAT_INPUT` commands |
| default\_member\_permissions? | ?string | Set of [permissions](/developers/topics/permissions) represented as a bit set |
| default\_permission? | boolean | Replaced by `default_member_permissions` and will be deprecated in the future. Indicates whether the command is enabled by default when the app is added to a guild. Defaults to `true` |
| type? | one of [application command type](/developers/interactions/application-commands#application-command-object-application-command-types) | Type of command, defaults `1` if not set |
| nsfw? | boolean | Indicates whether the command is [age-restricted](/developers/interactions/application-commands#agerestricted-commands) |
## Get Guild Application Command
/applications/[\{application.id}](/developers/resources/application#application-object)/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/commands/[\{command.id}](/developers/interactions/application-commands#application-command-object)
Fetch a guild command for your application. Returns an [application command](/developers/interactions/application-commands#application-command-object) object.
## Edit Guild Application Command
/applications/[\{application.id}](/developers/resources/application#application-object)/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/commands/[\{command.id}](/developers/interactions/application-commands#application-command-object)
All parameters for this endpoint are optional.
Edit a guild command. Updates for guild commands will be available immediately. Returns `200` and an [application command](/developers/interactions/application-commands#application-command-object) object. All fields are optional, but any fields provided will entirely overwrite the existing values of those fields.
###### JSON Params
| Field | Type | Description |
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name? | string | [Name of command](/developers/interactions/application-commands#application-command-object-application-command-naming), 1-32 characters |
| name\_localizations? | ?dictionary with keys in [available locales](/developers/reference#locales) | Localization dictionary for the `name` field. Values follow the same restrictions as `name` |
| description? | string | 1-100 character description |
| description\_localizations? | ?dictionary with keys in [available locales](/developers/reference#locales) | Localization dictionary for the `description` field. Values follow the same restrictions as `description` |
| options? | array of [application command option](/developers/interactions/application-commands#application-command-object-application-command-option-structure) | Parameters for the command, max of 25. Only for `CHAT_INPUT` commands |
| default\_member\_permissions? | ?string | Set of [permissions](/developers/topics/permissions) represented as a bit set |
| default\_permission? | boolean | Replaced by `default_member_permissions` and will be deprecated in the future. Indicates whether the command is enabled by default when the app is added to a guild. Defaults to `true` |
| nsfw? | boolean | Indicates whether the command is [age-restricted](/developers/interactions/application-commands#agerestricted-commands) |
## Delete Guild Application Command
/applications/[\{application.id}](/developers/resources/application#application-object)/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/commands/[\{command.id}](/developers/interactions/application-commands#application-command-object)
Delete a guild command. Returns `204 No Content` on success.
## Bulk Overwrite Guild Application Commands
/applications/[\{application.id}](/developers/resources/application#application-object)/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/commands
Takes a list of application commands, overwriting the existing command list for this application for the targeted guild. Returns `200` and a list of [application command](/developers/interactions/application-commands#application-command-object) objects.
This will overwrite **all** types of application commands: slash commands, user commands, and message commands.
###### JSON Params
| Field | Type | Description |
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| id? | snowflake | ID of the command, if known |
| name | string | [Name of command](/developers/interactions/application-commands#application-command-object-application-command-naming), 1-32 characters |
| name\_localizations? | ?dictionary with keys in [available locales](/developers/reference#locales) | Localization dictionary for the `name` field. Values follow the same restrictions as `name` |
| description | string | 1-100 character description |
| description\_localizations? | ?dictionary with keys in [available locales](/developers/reference#locales) | Localization dictionary for the `description` field. Values follow the same restrictions as `description` |
| options? | array of [application command option](/developers/interactions/application-commands#application-command-object-application-command-option-structure) | Parameters for the command |
| default\_member\_permissions? | ?string | Set of [permissions](/developers/topics/permissions) represented as a bit set |
| dm\_permission? | ?boolean | **Deprecated (use `contexts` instead)**; Indicates whether the command is available in DMs with the app, only for globally-scoped commands. By default, commands are visible. |
| default\_permission? | boolean | Replaced by `default_member_permissions` and will be deprecated in the future. Indicates whether the command is enabled by default when the app is added to a guild. Defaults to `true` |
| integration\_types | list of [integration types](/developers/resources/application#application-object-application-integration-types) | [Installation context(s)](/developers/resources/application#installation-context) where the command is available, defaults to `GUILD_INSTALL` (`[0]`) |
| contexts | list of [interaction context types](/developers/interactions/receiving-and-responding#interaction-object-interaction-context-types) | [Interaction context(s)](/developers/interactions/receiving-and-responding#interaction-object-interaction-context-types) where the command can be used, defaults to all contexts `[0,1,2]` |
| type? | one of [application command type](/developers/interactions/application-commands#application-command-object-application-command-types) | Type of command, defaults `1` if not set |
## Get Guild Application Command Permissions
/applications/[\{application.id}](/developers/resources/application#application-object)/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/commands/permissions
Fetches permissions for all commands for your application in a guild. Returns an array of [guild application command permissions](/developers/interactions/application-commands#application-command-permissions-object-guild-application-command-permissions-structure) objects.
## Get Application Command Permissions
/applications/[\{application.id}](/developers/resources/application#application-object)/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/commands/[\{command.id}](/developers/interactions/application-commands#application-command-object)/permissions
Fetches permissions for a specific command for your application in a guild. Returns a [guild application command permissions](/developers/interactions/application-commands#application-command-permissions-object-guild-application-command-permissions-structure) object.
## Edit Application Command Permissions
/applications/[\{application.id}](/developers/resources/application#application-object)/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/commands/[\{command.id}](/developers/interactions/application-commands#application-command-object)/permissions
This endpoint will overwrite existing permissions for the command in that guild
Edits command permissions for a specific command for your application in a guild and returns a [guild application command permissions](/developers/interactions/application-commands#application-command-permissions-object-guild-application-command-permissions-structure) object. Fires an [Application Command Permissions Update](/developers/events/gateway-events#application-command-permissions-update) Gateway event.
You can add up to 100 permission overwrites for a command.
This endpoint requires authentication with a Bearer token that has permission to manage the guild and its roles. For more information, read above about [application command permissions](/developers/interactions/application-commands#permissions).
Deleting or renaming a command will permanently delete all permissions for the command
###### JSON Params
| Field | Type | Description |
| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- |
| permissions | array of [application command permissions](/developers/interactions/application-commands#application-command-permissions-object-application-command-permissions-structure) | Permissions for the command in the guild |
## Batch Edit Application Command Permissions
/applications/[\{application.id}](/developers/resources/application#application-object)/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/commands/permissions
This endpoint has been disabled with [updates to command permissions (Permissions v2)](/developers/change-log#updated-command-permissions). Instead, you can [edit each application command permissions](/developers/interactions/application-commands#edit-application-command-permissions) (though you should be careful to handle any potential [rate limits](/developers/topics/rate-limits)).
# Interactions Overview
Source: https://docs.discord.com/developers/interactions/overview
Learn about Discord's interactive features including commands and message components.
Interactive features like commands and message components allows users to invoke an app natively within Discord. When a user engages with one of your app's interactive features, your app will receive an [interaction](/developers/interactions/receiving-and-responding#interaction-object).
This overview includes an overview of the main types of interactions, and steps for you to prepare your app to use and receive interactions. Reference documentation and details about handling interactions are in [Receiving and Responding](/developers/interactions/receiving-and-responding).
***
## Types of Interactions
There are different types of interactions in your app's toolbelt that can pick and choose to build engaging, interactive experiences in Discord.
### Commands
**[Application commands](/developers/interactions/application-commands)** provide users a native way to invoke an app in Discord. They often map to an app's core features or functionality.
When an app creates a command it can choose the command's type, which determines where it appears in the Discord client and the metadata the app will receive when the command is invoked. There are three types for application commands:
* **Slash commands** are the most common type of command and are accessed by typing `/` in the chat input, or by opening the command picker.
* **Message commands** are commands related to a message or a message's content. They're accessed by clicking on the context menu (the three dots) at the top-right of a message (or right clicking on a message), then navigating to the "Apps" section.
* **User commands** are commands that related to a user in Discord. They're accessed by right clicking on a user profile, then navigating to the "Apps" section.
* **Entry Point commands** are commands used as the primary way to launch [Activities](/developers/activities/overview) from the App Launcher.
Details about creating commands and handling command interactions are in the [Application Commands](/developers/interactions/application-commands) documentation.
### Message Components
**[Message components](/developers/components/reference)** are interactive elements that can be included in the content of a message that your app sends in Discord.
The main interactive components that apps can send in messages include:
* [Buttons](/developers/components/reference#button) are clickable components that can be customized with different styles, texts, and emoji.
* [Static select menus](/developers/components/reference#string-select) are components that a user can open to see a list of developer-defined select options with custom labels and descriptions.
* [Auto-populated select menus](/developers/components/reference#string-select) are a set of four different select components that are populated with contextual Discord resources, like a list of users or channels in a server.
A list of all message components and details on sending and receiving component interactions is in the [Message Components](/developers/components/reference) documentation.
### Modals
**[Modals](/developers/interactions/receiving-and-responding#interaction-response-object-modal)** are single-user pop-up interfaces that allow apps to collect form-like data. Modals can only be opened in response to a user invoking one of your app's commands or message components.
The only interactive component that modals can contain are [text inputs](/developers/components/reference#text-input), which allow users to fill out single-or-multi line form inputs.
Details about creating and using modals is in the [Receiving and Responding](/developers/interactions/receiving-and-responding#interaction-response-object-modal) documentation.
***
## Preparing for Interactions
When a user interacts with your app, you have the option for your app to receive interactions in two mutually-exclusive ways:
* WebSocket-based Gateway connection
* HTTP via outgoing webhooks
By default your app will receive interactions via a Gateway connection, but you can opt-in to HTTP-based interactions by adding a **Interactions Endpoint URL** to your app's settings. Technical details about handling interactions is in the [Receiving and Responding](/developers/interactions/receiving-and-responding) documentation.
### Configuring an Interactions Endpoint URL
A **Interactions Endpoint URL** is a public endpoint for your app where Discord can send your app HTTP-based interactions. If your app is using [Gateway](/developers/events/gateway)-based interactions, you don't need to configure an Interactions Endpoint URL.
#### Setting Up an Endpoint
Before you can add your Interactions Endpoint URL to your app, your endpoint must be prepared for two things ahead of time:
1. Acknowledging `PING` requests from Discord
2. Validate security-related request headers (`X-Signature-Ed25519` and `X-Signature-Timestamp`)
If either of these are not complete, your Interactions Endpoint URL will not be validated. Details on acknowledging PING requests and validating security-related headers are in the sections below.
#### Acknowledging PING requests
When adding your Interactions Endpoint URL, Discord will send a `POST` request with a `PING` payload with a `type: 1` to your endpoint. Your app is expected to acknowledge the request by returning a `200` response with a `PONG` payload (which has the same `type: 1`). Details about interaction responses are in the [Receiving and Responding documentation](/developers/interactions/receiving-and-responding).
You must provide a valid `Content-Type` when responding to `PING`s. See [here](/developers/reference#http-api) for further information.
To properly acknowledge a `PING` payload, return a `200` response with a payload of `type: 1`:
```py theme={"system"}
@app.route('/', methods=['POST'])
def my_command():
if request.json["type"] == 1:
return jsonify({
"type": 1
})
```
###### Validating Security Request Headers
The internet is a scary place, especially for people hosting public, unauthenticated endpoints. To receive interactions via HTTP, there are some security steps you **must** take before your app is eligible to receive requests.
Each interaction is sent with the following headers:
* `X-Signature-Ed25519` as a signature
* `X-Signature-Timestamp` as a timestamp
Using your favorite security library, you **must validate the request each time you receive an [interaction](/developers/interactions/receiving-and-responding#interaction-object)**. If the signature fails validation, your app should respond with a `401` error code.
Below are some code examples that show how to validate the headers sent in interactions requests.
**JavaScript**
```js theme={"system"}
const nacl = require("tweetnacl");
// Your public key can be found on your application in the Developer Portal
const PUBLIC_KEY = "APPLICATION_PUBLIC_KEY";
const signature = req.get("X-Signature-Ed25519");
const timestamp = req.get("X-Signature-Timestamp");
const body = req.rawBody; // rawBody is expected to be a string, not raw bytes
const isVerified = nacl.sign.detached.verify(
Buffer.from(timestamp + body),
Buffer.from(signature, "hex"),
Buffer.from(PUBLIC_KEY, "hex")
);
if (!isVerified) {
return res.status(401).end("invalid request signature");
}
```
**Python**
```py theme={"system"}
from nacl.signing import VerifyKey
from nacl.exceptions import BadSignatureError
# Your public key can be found on your application in the Developer Portal
PUBLIC_KEY = 'APPLICATION_PUBLIC_KEY'
verify_key = VerifyKey(bytes.fromhex(PUBLIC_KEY))
signature = request.headers["X-Signature-Ed25519"]
timestamp = request.headers["X-Signature-Timestamp"]
body = request.data.decode("utf-8")
try:
verify_key.verify(f'{timestamp}{body}'.encode(), bytes.fromhex(signature))
except BadSignatureError:
abort(401, 'invalid request signature')
```
In addition to ensuring your app validates security-related request headers at the time of saving your endpoint, Discord will also perform automated, routine security checks against your endpoint, including purposefully sending you invalid signatures. If you fail the validation, we will remove your interactions URL and alert you via email and System DM.
We highly recommend checking out our [Community Resources](/developers/developer-tools/community-resources#interactions) and the libraries found there. They not only provide typing for Interactions data models, but also include decorators for API frameworks like Flask and Express to make validation easy.
#### Adding an Interactions Endpoint URL
After you have a public endpoint to use as your app's Interactions Endpoint URL, you can add it to your app by going to your [app's settings](https://discord.com/developers/applications).
On the [**General Information** page](https://discord.com/developers/applications/select/information), look for the **Interactive Endpoint URL** field. Paste your public URL that is set up to acknowledge `PING` messages and correctly handles security-related signature headers.
***
## Handling Interactions
Once your app is prepared for interactions, you can explore the [Receiving and Responding](/developers/interactions/receiving-and-responding) documentation which goes into the technical details of handling interactions requests in your app.
# Receiving and Responding to Interactions
Source: https://docs.discord.com/developers/interactions/receiving-and-responding
An **[Interaction](/developers/interactions/receiving-and-responding#interaction-object)** is the message that your application receives when a user uses an application command or a message component.
For [Slash Commands](/developers/interactions/application-commands#slash-commands), it includes the values that the user submitted.
For [User Commands](/developers/interactions/application-commands#user-commands) and [Message Commands](/developers/interactions/application-commands#message-commands), it includes the resolved user or message on which the action was taken.
For [Message Components](/developers/components/reference) it includes identifying information about the component that was used. It will also include some metadata about how the interaction was triggered: the `guild_id`, `channel`, `member` and other fields. You can find all the values in our data models below.
### Interaction Object
###### Interaction Structure
| Field | Type | Description |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id | snowflake | ID of the interaction |
| application\_id | snowflake | ID of the application this interaction is for |
| type | [interaction type](/developers/interactions/receiving-and-responding#interaction-object-interaction-type) | Type of interaction |
| data?\* | [interaction data](/developers/interactions/receiving-and-responding#interaction-object-interaction-data) | Interaction data payload |
| guild? | [partial guild](/developers/resources/guild#guild-object) object | Guild that the interaction was sent from |
| guild\_id? | snowflake | Guild that the interaction was sent from |
| channel? | [partial channel](/developers/resources/channel#channel-object) object | Channel that the interaction was sent from |
| channel\_id? | snowflake | Channel that the interaction was sent from |
| member?\*\* | [guild member](/developers/resources/guild#guild-member-object) object | Guild member data for the invoking user, including permissions |
| user? | [user](/developers/resources/user#user-object) object | User object for the invoking user, if invoked in a DM |
| token | string | Continuation token for responding to the interaction |
| version | integer | Read-only property, always `1` |
| message? | [message](/developers/resources/message#message-object) object | For components or modals triggered by components, the message they were attached to |
| app\_permissions\*\*\* | string | Bitwise set of permissions the app has in the source location of the interaction |
| locale?\*\*\*\* | string | Selected [language](/developers/reference#locales) of the invoking user |
| guild\_locale? | string | [Guild's preferred locale](/developers/resources/guild#guild-object), if invoked in a guild |
| entitlements | array of [entitlement](/developers/resources/entitlement#entitlement-object) objects | For [monetized apps](/developers/monetization/overview), any entitlements for the invoking user, representing access to premium [SKUs](/developers/resources/sku) |
| authorizing\_integration\_owners | dictionary with keys of [application integration types](/developers/resources/application#application-object-application-integration-types) | Mapping of installation contexts that the interaction was authorized for to related user or guild IDs. See [Authorizing Integration Owners Object](/developers/interactions/receiving-and-responding#interaction-object-authorizing-integration-owners-object) for details |
| context? | [interaction context type](/developers/interactions/receiving-and-responding#interaction-object-interaction-context-types) | Context where the interaction was triggered from |
| attachment\_size\_limit | integer | Attachment size limit in bytes |
\* This is always present on application command, message component, and modal submit interaction types. It is optional for future-proofing against new interaction types
\*\* `member` is sent when the interaction is invoked in a guild, and `user` is sent when invoked in a DM
\*\*\* `app_permissions` includes `ATTACH_FILES | EMBED_LINKS | MENTION_EVERYONE` permissions for (G)DMs with other users, and additionally includes `USE_EXTERNAL_EMOJIS` for DMs with the app's bot user
\*\*\*\* This is available on all interaction types except PING
###### Interaction Type
| Name | Value |
| ---------------------------------- | ----- |
| PING | 1 |
| APPLICATION\_COMMAND | 2 |
| MESSAGE\_COMPONENT | 3 |
| APPLICATION\_COMMAND\_AUTOCOMPLETE | 4 |
| MODAL\_SUBMIT | 5 |
###### Interaction Context Types
Context in Discord where an interaction can be used, or where it was triggered from. Details about using interaction contexts for application commands is in the [commands context documentation](/developers/interactions/application-commands#interaction-contexts).
| Name | Type | Description |
| ---------------- | ---- | ------------------------------------------------------------------------------ |
| GUILD | 0 | Interaction can be used within servers |
| BOT\_DM | 1 | Interaction can be used within DMs with the app's bot user |
| PRIVATE\_CHANNEL | 2 | Interaction can be used within Group DMs and DMs other than the app's bot user |
###### Authorizing Integration Owners Object
The `authorizing_integration_owners` field includes details about the authorizing user or server for the installation(s) relevant to the interaction. For apps installed to a user, it can be used to tell the difference between the authorizing user and the user that triggered an interaction (like a message component).
A key will only be present if the following are true:
* The app has been authorized to the [installation context](/developers/resources/application#application-object-application-integration-types) corresponding to the key (`GUILD_INSTALL` or `USER_INSTALL`)
* The interaction is supported in the source [interaction context](/developers/interactions/receiving-and-responding#interaction-object-interaction-context-types) (`GUILD`, `BOT_DM`, or `PRIVATE_CHANNEL`) for the installation context corresponding to the key
* And for command invocations, the command must be supported in the installation context (using [`integration_types`](/developers/interactions/application-commands#contexts))
The values in `authorizing_integration_owners` depend on the key—
* If the key is `GUILD_INSTALL` (`"0"`), the value depends on the source of the interaction:
* The value will be the guild ID if the interaction is triggered from a server
* The value will be `"0"` if the interaction is triggered from a DM with the app's bot user
* If the key is `USER_INSTALL` (`"1"`), the value will be the ID of the authorizing user
###### Interaction Data
While the `data` field is guaranteed to be present for all [interaction types](/developers/interactions/receiving-and-responding#interaction-object-interaction-type) besides `PING`, its structure will vary. The following tables detail the inner `data` payload for each interaction type.
| Interaction Type | Interaction Data |
| ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| PING (`1`) | N / A |
| APPLICATION\_COMMAND (`2`) | [Application Command Data Structure](/developers/interactions/receiving-and-responding#interaction-object-application-command-data-structure) |
| MESSAGE\_COMPONENT (`3`) | [Message Component Data Structure](/developers/interactions/receiving-and-responding#interaction-object-message-component-data-structure) |
| APPLICATION\_COMMAND\_AUTOCOMPLETE (`4`) | [Application Command Data Structure](/developers/interactions/receiving-and-responding#interaction-object-application-command-data-structure) |
| MODAL\_SUBMIT (`5`) | [Modal Submit Data Structure](/developers/interactions/receiving-and-responding#interaction-object-modal-submit-data-structure) |
###### Application Command Data Structure
Sent in `APPLICATION_COMMAND` and `APPLICATION_COMMAND_AUTOCOMPLETE` interactions.
| Field | Type | Description |
| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| id | snowflake | [`ID`](/developers/interactions/application-commands#application-command-object-application-command-structure) of the invoked command |
| name | string | [`name`](/developers/interactions/application-commands#application-command-object-application-command-structure) of the invoked command |
| type | integer | [`type`](/developers/interactions/application-commands#application-command-object-application-command-structure) of the invoked command |
| resolved? | [resolved data](/developers/interactions/receiving-and-responding#interaction-object-resolved-data-structure) | Converted users + roles + channels + attachments |
| options?\* | array of [application command interaction data option](/developers/interactions/receiving-and-responding#interaction-object-application-command-interaction-data-option-structure) | Params + values from the user |
| guild\_id? | snowflake | ID of the guild the command is registered to |
| target\_id? | snowflake | ID of the user or message targeted by a [user](/developers/interactions/application-commands#user-commands) or [message](/developers/interactions/application-commands#message-commands) command |
\* This [can be partial](/developers/interactions/application-commands#autocomplete) when in response to `APPLICATION_COMMAND_AUTOCOMPLETE`
###### Message Component Data Structure
| Field | Type | Description |
| --------------- | ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| custom\_id | string | [`custom_id`](/developers/components/reference#anatomy-of-a-component-custom-id) of the component |
| component\_type | integer | [type](/developers/components/reference#component-object-component-types) of the component |
| values?\* | array of [select option values](/developers/components/reference#string-select-select-option-structure) | Values the user selected in a [select menu](/developers/components/reference#string-select) component |
| resolved? | [resolved data](/developers/interactions/receiving-and-responding#interaction-object-resolved-data-structure) | Resolved entities from selected options |
\* This is always present for select menu components
###### Modal Submit Data Structure
| Field | Type | Description |
| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- |
| custom\_id | string | The custom ID provided for the modal |
| components | array of [component interaction response](/developers/interactions/receiving-and-responding#interaction-object-component-interaction-response-structures) | Values submitted by the user |
| resolved? | [resolved data](/developers/interactions/receiving-and-responding#interaction-object-resolved-data-structure) | Resolved entities from selected options |
###### Component Interaction Response Structures
Response structures for both modal and message component interactions.
| Component |
| --------------------------------------------------------------------------------------------------------------------------- |
| [String Select](/developers/components/reference#string-select-string-select-interaction-response-structure) |
| [Text Input](/developers/components/reference#text-input-text-input-interaction-response-structure) |
| [User Select](/developers/components/reference#user-select-user-select-interaction-response-structure) |
| [Role Select](/developers/components/reference#role-select-role-select-interaction-response-structure) |
| [Mentionable Select](/developers/components/reference#mentionable-select-mentionable-select-interaction-response-structure) |
| [Channel Select](/developers/components/reference#channel-select-channel-select-interaction-response-structure) |
| [Text Display](/developers/components/reference#text-display-text-display-interaction-response-structure) |
| [Label](/developers/components/reference#label-label-interaction-response-structure) |
| [File Upload](/developers/components/reference#file-upload-file-upload-interaction-response-structure) |
| [Radio Group](/developers/components/reference#radio-group-interaction-response-structure) |
| [Checkbox Group](/developers/components/reference#checkbox-group-interaction-response-structure) |
| [Checkbox](/developers/components/reference#checkbox-interaction-response-structure) |
###### Resolved Data Structure
If data for a Member is included, data for its corresponding User will also be included.
| Field | Type | Description |
| ------------- | ---------------------------------------------------------------------------------------------- | ------------------------------- |
| users? | Map of Snowflakes to [user](/developers/resources/user#user-object) objects | IDs and User objects |
| members?\* | Map of Snowflakes to [partial member](/developers/resources/guild#guild-member-object) objects | IDs and partial Member objects |
| roles? | Map of Snowflakes to [role](/developers/topics/permissions#role-object) objects | IDs and Role objects |
| channels?\*\* | Map of Snowflakes to [partial channel](/developers/resources/channel#channel-object) objects | IDs and partial Channel objects |
| messages? | Map of Snowflakes to [partial messages](/developers/resources/message#message-object) objects | IDs and partial Message objects |
| attachments? | Map of Snowflakes to [attachment](/developers/resources/message#attachment-object) objects | IDs and attachment objects |
\* Partial `Member` objects are missing `user`, `deaf` and `mute` fields
\*\* Partial `Channel` objects only have `id`, `name`, `type`, `permissions`, `last_message_id`, `last_pin_timestamp`, `nsfw`, `parent_id`, `guild_id`, `flags`, `rate_limit_per_user`, `topic` and `position` fields. Threads will also have the `thread_metadata` field.
###### Application Command Interaction Data Option Structure
All options have names, and an option can either be a parameter and input value--in which case `value` will be set--or it can denote a subcommand or group--in which case it will contain a top-level key and another array of `options`.
`value` and `options` are mutually exclusive.
| Field | Type | Description |
| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| name | string | Name of the parameter |
| type | integer | Value of [application command option type](/developers/interactions/application-commands#application-command-object-application-command-option-type) |
| value? | string, integer, double, or boolean | Value of the option resulting from user input |
| options? | array of [application command interaction data option](/developers/interactions/receiving-and-responding#interaction-object-application-command-interaction-data-option-structure) | Present if this option is a group or subcommand |
| focused? | boolean | `true` if this option is the currently focused option for autocomplete |
### Message Interaction Object
This is sent on the [message object](/developers/resources/message#message-object) when the message is a response to an Interaction without an existing message.
This means responses to [Message Components](/developers/components/reference) do not include this property, instead including a [message reference](/developers/resources/message#message-reference-structure) object as components *always* exist on preexisting messages.
###### Message Interaction Structure
| Field | Type | Description |
| ------- | --------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id | snowflake | ID of the interaction |
| type | [interaction type](/developers/interactions/receiving-and-responding#interaction-object-interaction-type) | Type of interaction |
| name | string | Name of the [application command](/developers/interactions/application-commands#application-command-object-application-command-structure), including subcommands and subcommand groups |
| user | [user object](/developers/resources/user#user-object) | User who invoked the interaction |
| member? | [partial member](/developers/resources/guild#guild-member-object) object | Member who invoked the interaction in the guild |
## Receiving an Interaction
When a user interacts with your app, your app will receive an **[Interaction](/developers/interactions/receiving-and-responding#interaction-object)**. Your app can receive an interaction in one of two ways:
* Via [Interaction Create](/developers/events/gateway-events#interaction-create) gateway event
* Via outgoing webhook
These two methods are **mutually exclusive**; you can *only* receive Interactions one of the two ways. The `INTERACTION_CREATE` [Gateway Event](/developers/events/gateway-events#interaction-create) may be handled by connected clients, while the webhook method detailed below does not require a connected client.
If you want to receive interactions via HTTP-based outgoing webhooks, you must configure an Interactions Endpoint URL for your app. You can read about preparing and adding an Interactions Endpoint URL to your app in the [Preparing for Interactions](/developers/interactions/overview#preparing-for-interactions) section in Interactions Overview.
### Interaction Metadata
An [Interaction](/developers/interactions/receiving-and-responding#interaction-object) includes metadata to aid your application in handling it as well as `data` specific to the interaction type. You can find samples for each interaction type on their respective pages:
* [Slash Commands](/developers/interactions/application-commands#slash-commands-example-interaction)
* [User Commands](/developers/interactions/application-commands#user-commands-example-interaction)
* [Message Commands](/developers/interactions/application-commands#message-commands-example-interaction)
* [Message Components](/developers/components/using-message-components)
* [Modal Components](/developers/components/using-modal-components)
An explanation of all the fields can be found in our [data models](/developers/interactions/receiving-and-responding#interaction-object).
Now that you've gotten the data from the user, it's time to respond to them.
## Responding to an Interaction
Interactions--both receiving and responding--are webhooks under the hood. So responding to an Interaction is just like sending a webhook request!
Interaction responses have the same header requirements as normal HTTP API requests. See [here](/developers/reference#http-api) for further information.
There are a number of ways you can respond to an interaction:
### Interaction Response Object
###### Interaction Response Structure
| Field | Type | Description |
| ----- | ---------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- |
| type | [interaction callback type](/developers/interactions/receiving-and-responding#interaction-response-object-interaction-callback-type) | Type of response |
| data? | [interaction callback data](/developers/interactions/receiving-and-responding#interaction-response-object-interaction-callback-data-structure) | An optional response message |
###### Interaction Callback Type
| Name | Value | Description |
| ------------------------------------------ | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| PONG | 1 | ACK a `Ping` |
| CHANNEL\_MESSAGE\_WITH\_SOURCE | 4 | Respond to an interaction with a message |
| DEFERRED\_CHANNEL\_MESSAGE\_WITH\_SOURCE | 5 | ACK an interaction and edit a response later, the user sees a loading state |
| DEFERRED\_UPDATE\_MESSAGE\* | 6 | For components, ACK an interaction and edit the original message later; the user does not see a loading state |
| UPDATE\_MESSAGE\* | 7 | For components, edit the message the component was attached to |
| APPLICATION\_COMMAND\_AUTOCOMPLETE\_RESULT | 8 | Respond to an autocomplete interaction with suggested choices |
| MODAL\*\* | 9 | Respond to an interaction with a popup modal |
| PREMIUM\_REQUIRED | 10 | [**Deprecated**](/developers/change-log#premium-apps-new-premium-button-style-deep-linking-url-schemes); respond to an interaction with an upgrade button, only available for apps with [monetization](/developers/monetization/overview) enabled |
| LAUNCH\_ACTIVITY | 12 | Launch the Activity associated with the app. Only available for apps with [Activities](/developers/activities/overview) enabled |
\* Only valid for [component-based](/developers/components/reference) interactions
\*\* Not available for `MODAL_SUBMIT` and `PING` interactions.
###### Interaction Callback Data Structure
###### Messages
Not all message fields are currently supported.
| Field | Type | Description |
| ------------------ | -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| tts? | boolean | Whether the response is TTS |
| content? | string | Message content |
| embeds? | array of [embeds](/developers/resources/message#embed-object) | Supports up to 10 embeds |
| allowed\_mentions? | [allowed mentions](/developers/resources/message#allowed-mentions-object) | [Allowed mentions](/developers/resources/message#allowed-mentions-object) object |
| flags? \* | integer | [Message flags](/developers/resources/message#message-object-message-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) (only `SUPPRESS_EMBEDS`, `EPHEMERAL`, `IS_COMPONENTS_V2`, `IS_VOICE_MESSAGE`, and `SUPPRESS_NOTIFICATIONS` can be set) |
| components? | array of [components](/developers/components/reference#component-object) | Message components |
| attachments? \*\* | array of partial [attachment](/developers/resources/message#attachment-object) objects | Attachment objects with filename and description |
| poll? | [poll](/developers/resources/poll#poll-create-request-object) request object | Details about the poll |
\* If you create a callback with the [type](/developers/interactions/receiving-and-responding#interaction-response-object-interaction-callback-type) `DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE` the only valid [message flag](/developers/resources/message#message-object-message-flags) you may use is `EPHEMERAL`. If you'd like to create a component based message with `IS_COMPONENTS_V2` you must do that with the [edit original response](/developers/interactions/receiving-and-responding#edit-original-interaction-response) endpoint, not this one.
\*\* See [Uploading Files](/developers/reference#uploading-files) for details.
###### Autocomplete
| Field | Type | Description |
| ------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- |
| choices | array of [choices](/developers/interactions/application-commands#application-command-object-application-command-option-choice-structure) | autocomplete choices (max of 25 choices) |
###### Modal
| Field | Type | Description |
| ---------- | ------------------------------------------------------------------------ | ------------------------------------------------------------- |
| custom\_id | string | Developer-defined identifier for the modal, 1-100 characters |
| title | string | Title of the popup modal, max 45 characters |
| components | array of [components](/developers/components/reference#component-object) | Between 1 and 5 (inclusive) components that make up the modal |
If your application responds with user data, you should use [`allowed_mentions`](/developers/resources/message#allowed-mentions-object) to filter which mentions in the content actually ping.
## Interaction Callback
When responding to an interaction received, you can make a `POST` request to `/interactions///callback`. `interaction_id` is the unique id of that individual Interaction from the received payload. `interaction_token` is the unique token for that interaction from the received payload.
If you are receiving Interactions over the gateway, you **have to respond via HTTP**. Responses to Interactions **are not sent as commands over the gateway**.
**If you send this request for an interaction received over HTTP, respond to the original HTTP request with a 202 and no body.**
```py theme={"system"}
import requests
url = "https://discord.com/api/v10/interactions///callback"
json = {
"type": 4,
"data": {
"content": "Congrats on sending your command!"
}
}
r = requests.post(url, json=json)
```
Interaction `tokens` are valid for **15 minutes** and can be used to send followup messages but you **must send an initial response within 3 seconds of receiving the event**. If the 3 second deadline is exceeded, the token will be invalidated.
If you receive interactions over HTTP, your server can also respond to the received `POST` request. You'll want to respond with a `200` status code (if everything went well), as well as specifying a `type` and `data`, which is an [Interaction Response](/developers/interactions/receiving-and-responding#interaction-response-object) object:
```py theme={"system"}
@app.route('/', methods=['POST'])
def my_command():
if request.json["type"] == 1:
return jsonify({
"type": 1
})
else:
return jsonify({
"type": 4,
"data": {
"tts": False,
"content": "Congrats on sending your command!",
"embeds": [],
"allowed_mentions": { "parse": [] }
}
})
```
###### Interaction Callback Response Object
| Field | Type | Description |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------- |
| interaction | [interaction callback object](/developers/interactions/receiving-and-responding#interaction-callback-interaction-callback-object) | The interaction object associated with the interaction response. |
| resource? | [interaction resource object](/developers/interactions/receiving-and-responding#interaction-callback-interaction-callback-resource-object) | The resource that was created by the interaction response. |
###### Interaction Callback Object
| Field | Type | Description |
| ----------------------------- | --------- | --------------------------------------------------------------------------------------------------------- |
| id | snowflake | ID of the interaction |
| type | integer | [Interaction type](/developers/interactions/receiving-and-responding#interaction-object-interaction-type) |
| activity\_instance\_id? | string | Instance ID of the Activity if one was launched or joined |
| response\_message\_id? | snowflake | ID of the message that was created by the interaction |
| response\_message\_loading? | boolean | Whether the message is in a loading state |
| response\_message\_ephemeral? | boolean | Whether the response message is ephemeral |
###### Interaction Callback Resource Object
| Field | Type | Description |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| type | integer | [Interaction callback type](/developers/interactions/receiving-and-responding#interaction-response-object-interaction-callback-type) |
| activity\_instance?\* | [Activity instance resource](/developers/interactions/receiving-and-responding#interaction-callback-interaction-callback-activity-instance-resource) | Represents the Activity launched by this interaction. |
| message?\*\* | [message object](/developers/resources/message#message-object) | Message created by the interaction. |
\* Only present if type is `LAUNCH_ACTIVITY`.
\*\* Only present if type is either `CHANNEL_MESSAGE_WITH_SOURCE` or `UPDATE_MESSAGE`.
###### Interaction Callback Activity Instance Resource
| Field | Type | Description |
| ----- | ------ | ---------------------------------------------------------- |
| id | string | Instance ID of the Activity if one was launched or joined. |
## Followup Messages
Sometimes, you want to send followup messages to a user after responding to an interaction. Or, you may want to edit your original response. Whether you receive Interactions over the gateway or by outgoing webhook, you can use the following endpoints to edit your initial response or send followup messages:
* [`PATCH /webhooks///messages/@original`](/developers/interactions/receiving-and-responding#edit-original-interaction-response) to edit your initial response to an Interaction
* [`DELETE /webhooks///messages/@original`](/developers/interactions/receiving-and-responding#delete-original-interaction-response) to delete your initial response to an Interaction
* [`POST /webhooks//`](/developers/interactions/receiving-and-responding#create-followup-message) to send a new followup message
* [`PATCH /webhooks///messages/`](/developers/interactions/receiving-and-responding#edit-followup-message) to edit a message sent with that `token`
Interactions webhooks share the same rate limit properties as normal webhooks.
Interaction tokens are valid for **15 minutes**, meaning you can respond to an interaction within that amount of time.
### Endpoints
The endpoints below are not bound to the application's [Global Rate Limit](/developers/topics/rate-limits#global-rate-limit).
## Create Interaction Response
/interactions/[\{interaction.id}](/developers/interactions/receiving-and-responding#interaction-object)/[\{interaction.token}](/developers/interactions/receiving-and-responding#interaction-object)/callback
Create a response to an Interaction. Body is an [interaction response](/developers/interactions/receiving-and-responding#interaction-response-object). Returns `204` unless `with_response` is set to `true` which returns `200` with the body as [interaction callback response](/developers/interactions/receiving-and-responding#interaction-callback-interaction-callback-response-object).
This endpoint also supports file attachments similar to the webhook endpoints. Refer to [Uploading Files](/developers/reference#uploading-files) for details on uploading files and `multipart/form-data` requests.
###### Query String Params
| Field | Type | Description |
| --------------- | ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| with\_response? | [boolean](/developers/reference#boolean-query-strings) | Whether to include an [interaction callback object](/developers/interactions/receiving-and-responding#interaction-callback-interaction-callback-response-object) as the response |
## Get Original Interaction Response
/webhooks/[\{application.id}](/developers/resources/application#application-object)/[\{interaction.token}](/developers/interactions/receiving-and-responding#interaction-object)/messages/@original
Returns the initial Interaction response. Functions the same as [Get Webhook Message](/developers/resources/webhook#get-webhook-message).
## Edit Original Interaction Response
/webhooks/[\{application.id}](/developers/resources/application#application-object)/[\{interaction.token}](/developers/interactions/receiving-and-responding#interaction-object)/messages/@original
Edits the initial Interaction response. Functions the same as [Edit Webhook Message](/developers/resources/webhook#edit-webhook-message).
## Delete Original Interaction Response
/webhooks/[\{application.id}](/developers/resources/application#application-object)/[\{interaction.token}](/developers/interactions/receiving-and-responding#interaction-object)/messages/@original
Deletes the initial Interaction response. Returns `204 No Content` on success.
## Create Followup Message
/webhooks/[\{application.id}](/developers/resources/application#application-object)/[\{interaction.token}](/developers/interactions/receiving-and-responding#interaction-object)
Apps are limited to 5 followup messages per interaction if it was initiated from a user-installed app and isn't installed in the server (meaning the [authorizing integration owners object](/developers/interactions/receiving-and-responding#interaction-object-authorizing-integration-owners-object) only contains `USER_INSTALL`)
Create a followup message for an Interaction. Functions the same as [Execute Webhook](/developers/resources/webhook#execute-webhook), but `wait` is always true. The `thread_id`, `avatar_url`, and `username` parameters are not supported when using this endpoint for interaction followups. You can use the `EPHEMERAL` [message flag](/developers/resources/message#message-object-message-flags) `1 << 6` (64) to send a message that only the user can see. You can also use the `IS_COMPONENTS_V2` [message flag](/developers/resources/message#message-object-message-flags) `1 << 15` (32768) to send a [component](/developers/components/reference)-based message.
When using this endpoint directly after responding to an interaction with `DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE`, this endpoint will function as [Edit Original Interaction Response](/developers/interactions/receiving-and-responding#edit-original-interaction-response) for backwards compatibility. In this case, no new message will be created, and the loading message will be edited instead. The ephemeral flag will be ignored, and the value you provided in the initial defer response will be preserved, as an existing message's ephemeral state cannot be changed. This behavior is deprecated, and you should use the [Edit Original Interaction Response](/developers/interactions/receiving-and-responding#edit-original-interaction-response) endpoint in this case instead.
## Get Followup Message
/webhooks/[\{application.id}](/developers/resources/application#application-object)/[\{interaction.token}](/developers/interactions/receiving-and-responding#interaction-object)/messages/[\{message.id}](/developers/resources/message#message-object)
Returns a followup message for an Interaction. Functions the same as [Get Webhook Message](/developers/resources/webhook#get-webhook-message).
## Edit Followup Message
/webhooks/[\{application.id}](/developers/resources/application#application-object)/[\{interaction.token}](/developers/interactions/receiving-and-responding#interaction-object)/messages/[\{message.id}](/developers/resources/message#message-object)
Edits a followup message for an Interaction. Functions the same as [Edit Webhook Message](/developers/resources/webhook#edit-webhook-message).
## Delete Followup Message
/webhooks/[\{application.id}](/developers/resources/application#application-object)/[\{interaction.token}](/developers/interactions/receiving-and-responding#interaction-object)/messages/[\{message.id}](/developers/resources/message#message-object)
Deletes a followup message for an Interaction. Returns `204 No Content` on success.
# Discord Developer Platform
Source: https://docs.discord.com/developers/intro
Build bots and integrations on Discord, or connect your game with rich presence, voice chat, and more.
## What do you want to build?
}>
Build automated apps, commands, and integrations that run inside Discord servers.
}>
Create multiplayer games and social experiences that launch directly inside Discord.
}>
Add rich presence, voice chat, and social features to your game across all platforms.
## New to Discord development?
}>
Understand what Discord apps are, the different types, and how to get started.
}>
Follow a step-by-step guide to build and run your first Discord bot.
## Resources
}>
Get support, API announcements, and participate in developer events.
}>
Report issues with the Discord API and SDKs.
}>
Find articles, FAQs, and reach out to Discord's developer support team.
# Enabling Monetization
Source: https://docs.discord.com/developers/monetization/enabling-monetization
Step-by-step guide to meet eligibility requirements and enable monetization.
Before you can add monetization to your app, you must ensure that your app and team meet the eligibility criteria.
## Steps to Enable Monetization
1. Set up your developer team and app to be eligible for monetization
2. Complete the eligibility criteria for monetization
3. Set up developer team payouts to get paid
4. Create your premium offering
5. Implement monetization in your app
6. Start offering your premium features
Once these are complete, you can [create SKUs](/developers/monetization/managing-skus#creating-a-sku) to represent your premium offerings and [add support for your premium offering](/developers/monetization/enabling-monetization#step-5-implement-monetization-in-your-app) in your app.
## Step 1. Set Up Your Developer Team and App
Before monetization can be enabled, you will need:
* A [team](/developers/topics/teams) in the developer portal. If you don't have one, you can [create one on the Teams page](https://discord.com/developers/teams)
* A [verified app](https://support-dev.discord.com/hc/en-us/articles/23926564536471-How-Do-I-Get-My-App-Verified) that is *owned by that team*
* Your app and team must be eligible for monetization. See the Eligibility Checklist below for details.
## Step 2. Complete the Eligibility Checklist
Before you can start creating SKUs and offering payments in your app, your app and team must be eligible for monetization. When a team owner enables monetization, they'll be taken through a series of steps and checks to ensure the following criteria are met:
* App must be verified
* App belongs to a developer team
* Team owner must be at least 18 years old
* Team must have verified emails and 2FA set up
* App uses slash commands, or has been approved for the privileged `Message Content` intent
* App has a link to your Terms of Service
* This document is an agreement between you and users governing the use of your app.
* App has a link to your Privacy Policy
* This document should clearly and accurately describe to users of your app the user data you collect and how you use and share such data with us and third parties, consistent with our Developer Terms of Service and Developer Policy.
* App must not contain any harmful or bad language in the name, description, commands, or role connection metadata.
* Payouts must be set up with a valid payment method
* Agreement to the [Monetization Terms](https://support.discord.com/hc/articles/5330075836311) and [Discord Developer Policy](https://support-dev.discord.com/hc/en-us/articles/8563934450327-Discord-Developer-Policy).
## Step 3. Set Up Team Payouts
Let's set up Team Payouts so you can get paid! Discord processes all payouts through Stripe, so part of setting up payouts will go through Stripe's onboarding flow.
* Only the owner of the team can enable payout settings for the team.
* Once your app has made its first \$100 it will become eligible for payout.
* A review will be conducted and if everything looks good, your team will begin to receive payouts.
#### If You are Based in the United States, European Union, or United Kingdom
* Click on [your team](https://discord.com/developers/teams) on the Teams page.
* Select **Payout Settings**.
* If you do not see **Payout Settings**, you are not the owner of the team. Only the owner of the team can enable payout settings for the team.
* Complete the onboarding flow through Stripe.
#### If You are Based Outside of the United States, European Union, or United Kingdom
Premium Apps is not currently available outside of these regions. These features will be made available to more regions soon.
For more information, read the [Premium Apps Payouts](https://support-dev.discord.com/hc/articles/17299902720919) Help Center article.
## Step 4: Create Your Premium Offering
You are now ready to start setting up your SKUs and offering premium features in your app. Check out our guide on [Managing your Premium Offerings](/developers/monetization/managing-skus#creating-a-sku) to create one-time purchases and subscriptions for your app.
## Step 5: Implement Monetization in your App
Now that you've set up your app for monetization, you can start adding code to support your premium features. We have guides for the following monetization strategies:
}>
Learn how to start and manage recurring subscriptions within your app.
}>
Learn how to implement one-time purchases in your app.
## Step 6: Start Offering Your Premium Features
Congratulations! You've successfully set up your app for monetization. Now you can start earning money from your app and providing premium features to your users.
You can now [link to your Store](/developers/monetization/managing-skus#linking-to-your-store) page, [link to a specific SKU](/developers/monetization/managing-skus#linking-to-a-specific-sku), or [include a premium styled button in Message Components](/developers/monetization/managing-skus#responding-with-a-premium-button) to allow your users to make purchases.
# Implementing App Subscriptions
Source: https://docs.discord.com/developers/monetization/implementing-app-subscriptions
Learn how to implement recurring user and guild subscriptions for premium functionality.
Charge users for premium app functionality with a recurring user or guild subscription.
* Before you can add an app subscription to your app, you must [Enable Monetization](/developers/monetization/enabling-monetization) for your app.
* Once you've confirmed eligibility for your app and team, you will be able to set up a [SKU](/developers/resources/sku) (stock-keeping unit) to represent your subscription.
***
## Types of Subscriptions
When creating subscriptions, you will need to choose between user or guild subscriptions:
* **User Subscriptions**: Offers premium features to an individual user across any server where your app is installed.
* **Guild Subscriptions**: Provides premium benefits to all members within a specific server.
***
## How App Subscriptions Work
* When a user purchases your subscription SKU, Discord creates an [Entitlement](/developers/resources/entitlement) for the user (or guild) and that specific Subscription [SKU](/developers/resources/sku).
* You will receive an `ENTITLEMENT_CREATE` event via the Gateway.
* This entitlement will be available via the `LIST Entitlements` API endpoint.
* This entitlement will be available on `Interaction Payloads` initiated from the entitled user or users in a guild (for guild subscriptions).
* This subscription will be available via the `LIST Subscriptions` API endpoint.
* This entitlement is granted indefinitely until the user decides to cancel their subscription. `ends_at` will be null.
* When a user cancels their subscription, your app will not receive any entitlement events.
* When a subscription ends, the entitlement to the subscription will end. Developers will receive an `ENTITLEMENT_UPDATE` event with an `ends_at` timestamp indicating when the subscription ended.
### Using Subscription Events for the Subscription Lifecycle
Because entitlements are granted indefinitely and don't update on renewal or cancellation, you can use subscription events to track the lifecycle of a subscription.
This is not a complete list of when events may occur. You should use the presence of an entitlement to determine if a user has access to your premium features. The Subscription API and related events are intended for reporting and lifecycle management purposes and **should not be used as the source of truth for whether a user has access to your premium features**.
| Event Name | Subscription Behavior | Updated Fields |
| --------------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- |
| `SUBSCRIPTION_CREATE` | Subscription is created | `status` is either `0 (active)` if an entitlement has been granted or `1 (inactive)` if an entitlement has not yet been granted |
| `SUBSCRIPTION_UPDATE` | Subscription is granted an entitlement | `status` is `0 (active)` |
| `SUBSCRIPTION_UPDATE` | Subscription is renewed | `current_period_start`, `current_period_end` timestamps updated |
| `SUBSCRIPTION_UPDATE` | Subscription is upgraded or downgraded | `sku_ids`, `entitlement_ids`, `renewal_sku_ids` may be updated |
| `SUBSCRIPTION_UPDATE` | Subscription is canceled | `canceled_at` timestamp updated, `status` is `2 (ending)` |
| `SUBSCRIPTION_UPDATE` | Subscription ends | `status` is `1 (inactive)`, this event is processed asynchronously and will not be immediate |
| `SUBSCRIPTION_UPDATE` | Subscription is resumed/uncanceled by user | `status` is `0 (active)` |
***
## Working with Entitlements
When a user purchases a subscription, an entitlement is created. [Entitlements](/developers/resources/entitlement) represent the user's access to your app's premium features.
Depending on your app's features, you can use a combination of [Gateway events](/developers/events/gateway-events#entitlements), the [Entitlement HTTP API](/developers/resources/entitlement), and [interaction payloads](/developers/interactions/receiving-and-responding) to keep track of user and guild entitlements and grant features to users who are subscribed to your app.
### Accessing Entitlements with Gateway Events
When users make a purchase in your app, Discord will emit [Entitlement Gateway events](/developers/events/gateway-events#entitlements).
For subscription SKUs, you will receive the following entitlement events:
| Event | Description |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `ENTITLEMENT_CREATE` | When a user is granted an entitlement to your app's subscription SKU |
| `ENTITLEMENT_UPDATE` | When an entitlement to a subscription SKU ends |
| `ENTITLEMENT_DELETE` | When Discord refunds a subscription, removes an entitlement, or when a developer [deletes a Test Entitlement](/developers/resources/entitlement#delete-test-entitlement) |
### Accessing Entitlements with the HTTP API
For apps requiring background processing or not solely reliant on interactions, keeping track of entitlements is essential. You can utilize the [List Entitlements](/developers/resources/entitlement#list-entitlements) endpoint to list active and expired entitlements. Your app can filter entitlements by a specific user or guild by using the `?user_id=XYZ` or `?guild_id=XYZ` query params.
For example, you might keep track of entitlements in a database and check if a user still has access to a specific SKU before performing a cron job or other task.
### Accessing Entitlements on Interaction Payloads
Entitlements are also available on the `Interaction Payload` when a user interacts with your app. You can find the entitlements on the `entitlements` field of the `Interaction Payload` when [receiving and responding to interactions](/developers/interactions/receiving-and-responding). You can use this field to determine if the user or guild is subscribed to your app.
### Accessing Entitlements with the Embedded App SDK
When using the [Embedded App SDK](/developers/developer-tools/embedded-app-sdk) to build an [Activity](/developers/activities/overview), you can also [access a user's entitlements](/developers/developer-tools/embedded-app-sdk#getentitlements). Check out the [Implementing In-App Purchases for Activities](/developers/monetization/implementing-iap-for-activities) guide to learn more about monetization with the Embedded App SDK.
***
## Prompting Users to Subscribe
### Responding with a Premium Button
[Responding with a premium button](/developers/monetization/managing-skus#responding-with-a-premium-button) gives you the ability to prompt users to subscribe to your app when they attempt to use a premium feature without a subscription.
You can do this by sending a message with a [button](/developers/components/reference#button) with a [premium style](/developers/components/reference#button-button-styles) and a `sku_id` that allows the user to upgrade to your premium offering.
### Starting a Purchase from an Activity
If you are using the [Embedded App SDK](/developers/developer-tools/embedded-app-sdk) to build an [Activity](/developers/activities/overview), you can also launch the purchase flow for a specific SKU using the SDK. Check out the [Implementing In-App Purchases for Activities](/developers/monetization/implementing-iap-for-activities) guide to learn more about monetization with the Embedded App SDK.
### Purchasing from the Store Page
Users can start, upgrade, or downgrade their subscription from your app's [Store](/developers/monetization/managing-skus#viewing-your-store-page) page. You can link directly to your Store page using our [Application Directory Store URL scheme](/developers/monetization/managing-skus#linking-to-your-store).
***
## Supporting Subscriptions
To support subscriptions in your app, you need to [create a subscription SKU](/developers/monetization/managing-skus#creating-a-sku) and handle the following scenarios:
### Starting a new subscription
When a user subscribes to a new subscription, you will receive the following events:
| Event | Event Trigger |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SUBSCRIPTION_CREATE` | when the subscription is initially created. `status` is `0 (active)` if the entitlement has been granted or `1 (inactive)` if the entitlement has not yet been granted. |
| `ENTITLEMENT_CREATE` | when the user is granted an entitlement for the new subscription |
| `SUBSCRIPTION_UPDATE` | when the subscription is updated with the `entitlement_ids`, `renewal_sku_ids`, and `status` (`0 (active)`) |
### Cancelling an existing subscription
Users can cancel their subscription at any time from their Subscription settings.
When a user cancels their subscription, you will receive the following events:
| Event | Event Trigger |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `SUBSCRIPTION_UPDATE` | when the subscription is updated to end with a `status` of `2 (ending)` and `canceled_at` is set to the timestamp the user canceled |
The user's subscription and entitlement are still valid until the subscription `current_period_end` is reached.
If the subscription is not resumed before the subscription `current_period_end`, it will end and you will receive the following events:
| Event | Event Trigger |
| --------------------- | -------------------------------------------------------------------------- |
| `ENTITLEMENT_UPDATE` | when the current entitlement ends. `ends_at` gets updated with a timestamp |
| `SUBSCRIPTION_UPDATE` | when the subscription is updated with the `status` of `1 (inactive)` |
### Resuming a cancelled subscription
Users can resume their subscription at any time before the `current_period_end` is reached in their Subscription settings.
When a user resumes their subscription, you will receive the following events:
| Event | Event Trigger |
| --------------------- | --------------------------------------------------------------------------------------------------------- |
| `SUBSCRIPTION_UPDATE` | when the subscription is set to continue with a `status` of `0 (active)` and `canceled_at` is set to null |
***
## Supporting Upgrades and Downgrades
If you offer multiple subscription tiers in your app, users can upgrade or downgrade their subscription at any time from your [Store page](/developers/monetization/managing-skus#viewing-your-store-page) or their App Subscription settings.
To create multiple subscription tiers, you will need to [create multiple subscription SKUs](/developers/monetization/managing-skus#creating-a-sku) and support the following scenarios in your app:
### Upgrading an existing subscription
If a user is on a lower tier subscription and upgrades to a subscription tier that is the same price or higher, the user is charged the difference in price between the two subscriptions and the subscription period resets at the time of upgrading.
When the subscription is upgraded, the current entitlement for the lower tier will end immediately and you will receive the following events:
| Event | Event Trigger |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `ENTITLEMENT_UPDATE` | when the current entitlement ends. `ends_at` gets updated with a timestamp |
| `ENTITLEMENT_CREATE` | when a new entitlement is created for the upgrade subscription SKU |
| `SUBSCRIPTION_UPDATE` | when the subscription is updated with the new `entitlement_ids`, `sku_ids`, `current_period_start`, `current_period_end` |
### Downgrading an existing subscription
If a user is on a higher tier subscription and downgrades to a lower tier subscription, the user is not charged immediately because the price is lower than what was already paid.
The user has already paid for their current plan until `subscription.current_period_end` so their current plan will be valid until then and you will receive the following event:
| Event | Event Trigger |
| --------------------- | ------------------------------------------------------------------------------------------------ |
| `SUBSCRIPTION_UPDATE` | when the subscription is updated to reflect the renewal SKU ID in `subscription.renewal_sku_ids` |
Once the user's current subscription expires on `subscription.current_period_end`, you will receive the following events:
| Event | Event Trigger |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `ENTITLEMENT_UPDATE` | when the current entitlement ends. `ends_at` gets updated with a timestamp |
| `ENTITLEMENT_CREATE` | when a new entitlement is created for the downgraded subscription SKU |
| `SUBSCRIPTION_UPDATE` | when the subscription is updated with the new `entitlement_ids`, `sku_ids`, `current_period_start`, `current_period_end` |
***
## Using the Subscription API
When implementing monetization, [Entitlements](/developers/resources/entitlement) should be considered the source of truth for a user's access to a specific SKU. The Subscription API is intended for reporting and lifecycle management purposes that happen outside the flow of a user's interaction with your app.
You can use the [Subscription API](/developers/resources/subscription) to check on the status of your app subscriptions. This API allows you to list subscriptions by user for reporting purposes and to check on the status of subscriptions without having to access entitlements directly.
* [List SKU Subscriptions](/developers/resources/subscription#list-sku-subscriptions): List all subscriptions for a specific SKU in your app.
* [Get SKU Subscription](/developers/resources/subscription#get-sku-subscription): Get a specific subscription in your app.
* [Subscription Gateway events](/developers/events/gateway-events#subscriptions): Discord will emit gateway events when a subscription is created, updated, and very rarely, deleted.
***
## Testing Your App Subscription Implementation
### Using Test Entitlements
You can test your implementation by [creating](/developers/resources/entitlement#create-test-entitlement) and [deleting](/developers/resources/entitlement#delete-test-entitlement) test entitlements. These entitlements will allow you to test your premium offering in both a subscribed and unsubscribed state as a user or guild.
This method will not let you test out the full payment flow in Discord but will allow you to test your app's behavior when a user is subscribed or unsubscribed. See [Using Live Entitlements](/developers/monetization/implementing-app-subscriptions#using-live-entitlements) if you'd like to see the full payment flow.
Test Entitlements do not have a `starts_at` or `ends_at` field as they are valid until they are deleted.
### Using Live Entitlements
If you'd like to test the full payment flow for your app, you can do so by interacting with your Store page or a [premium styled button](/developers/monetization/implementing-app-subscriptions#prompting-users-to-subscribe). Any team members associated with your app will automatically see a 100% discount on the price of the subscription, allowing you to purchase without the use of live payment method.
After checkout, you will have a live subscription. This subscription will renew until canceled and can be used in testing subscription renewals in your app. If you cancel this subscription, it will remain an active entitlement until the end of the subscription billing period, represented by the `current_period_end` field on the [Subscription](/developers/resources/subscription#subscription-object).
You can only delete entitlements created using the [create entitlement](/developers/resources/entitlement#create-test-entitlement) endpoint. If you need to toggle access to your premium features during your development process, it is best to use Test Entitlements.
# Implementing In-App Purchases for Activities
Source: https://docs.discord.com/developers/monetization/implementing-iap-for-activities
Step-by-step guide to add monetization to Discord Activities.
In-App Purchases (IAP) for [Activities](/developers/activities/overview) allows developers to easily monetize their Activity by allowing users to buy premium subscriptions or items natively in Discord. This guide will walk you through the process of implementing monetization using the [Embedded App SDK](/developers/developer-tools/embedded-app-sdk).
Before you can add premium products with the Embedded App SDK in an Activity, you must [Enable Monetization](/developers/monetization/enabling-monetization) for your app.
***
## Key Concepts
Before implementing monetization in your app, it's important to understand the features of Discord you'll be working with:
* **SKUs**: Represent your app's premium products.
* **Entitlements**: Represent the user's access to your premium products.
* **Subscriptions**: Represent an ongoing agreement for a user to pay for an entitlement on a recurring basis until canceled.
You can learn more about these concepts in the [Monetization Overview](/developers/monetization/overview).
***
## Working with SKUs
SKUs represent the premium products you offer in your app. Before you can start implementing monetization in your app, you will need to create a SKU for each premium product.
To learn more about creating and managing SKUs and the different types of SKUs you can create, see the [Managing SKUs](/developers/monetization/managing-skus) guide.
### Publishing SKUs for Activities
When publishing SKUs, you can choose to publish them to your **Store and the API** or **API Only**. Which method you select will depend on the purchase experience you want to offer your users.
#### Published to Store and the API
* Your SKUs will be visible to users in the Discord client in your [app's store](/developers/monetization/managing-skus#viewing-your-store-page) or your Activity's custom storefront.
* Your users will be able to purchase them directly from the Discord client without having your Activity open so you should handle the purchase flow accordingly.
#### Published to API Only
* Your SKUs will **not** be visible to users in your [app's store](/developers/monetization/managing-skus#viewing-your-store-page), and users will only be able to purchase them through your Activity's custom storefront.
Next, we'll cover how to create and render your own custom storefront in your Activity.
***
## Building a Storefront
Once you have created your SKUs, you will need to build and render your own custom storefront in your Activity to display your premium products to users. This section will guide you through the process of listing SKUs, mapping SKUs to your premium perks, and displaying prices.
### Listing SKUs
To fetch the list of products for displaying in your Activity, you can call the [`getSkus()`](/developers/developer-tools/embedded-app-sdk#getskus) command from the [Embedded App SDK](/developers/developer-tools/embedded-app-sdk).
You can also fetch SKUs using the [HTTP API](/developers/resources/sku#list-skus).
### Mapping SKUs to Your Premium Perks
When a user purchases a SKU, an entitlement is created. Entitlements represent the user's access to a specific SKU. You will need to map your SKUs to the premium perks you are offering in your application to ensure users receive the correct perks when they purchase a SKU.
When mapping SKUs to premium perks, keep the following in mind:
* SKUs should be mapped to product based on `id` attribute, **not** other attributes (such as `name`)
* SKUs can never be deleted, so once purchased it cannot be revoked and should always be mapped to some perk or product in your application
### Formatting and Displaying Prices
The methods for listing SKUs automatically localize currency and prices.
This means `sku.price` has a number `amount` attribute and a string `currency` attribute. Properly rendering prices with proper currency can be challenging, given the large number of currencies. The Embedded App SDK provides a `PriceUtils` utility to make this easier.
```javascript theme={"system"}
import {PriceUtils} from '@discord/embedded-app-sdk';
const displayPrice = PriceUtils.formatPrice(sku.price);
console.log(`The price is ${displayPrice}!`);
```
***
## Working with Entitlements
When a user purchases a SKU, an entitlement is created. [Entitlements](/developers/resources/entitlement) represent the user's access to your premium product.
Depending on your app's features, you can use a combination of the [Embedded App SDK events](/developers/developer-tools/embedded-app-sdk#getentitlements), [Gateway events](/developers/events/gateway-events#entitlements), the [Entitlement HTTP API](/developers/resources/entitlement), and [interaction payloads](/developers/interactions/receiving-and-responding) to keep track of entitlements to users who have purchased items in your app.
### Fetching Entitlements with the Embedded App SDK
Use the [`getEntitlements()`](/developers/developer-tools/embedded-app-sdk#getentitlements) SDK command to fetch a list of entitlements for a user. This command will return a list of entitlement objects that represent the user's access to your premium products.
### Handling Subscription Entitlements
When a user purchases a subscription SKU, an entitlement is created. For more information on handling subscription entitlements, see the [Implementing App Subscriptions](/developers/monetization/implementing-app-subscriptions) guide.
### Handling One-Time Purchase Entitlements
When a user purchases a one-time purchase SKU, an entitlement is created. For more information on handling one-time purchase entitlements, see the [Implementing One-Time Purchases](/developers/monetization/implementing-one-time-purchases) guide.
#### One-Time Purchases: Consumable and Durable Items
It is common in Activities to have consumable or one-time-use items, such as a single-use potion or power-up.
When a user purchases a consumable SKU, an entitlement is created. This entitlement will be marked as `consumed: false`. Your application should process the item purchase and [consume the entitlement](/developers/resources/entitlement#consume-an-entitlement) as soon as possible to grant the user the perks associated with the item.
If you want to offer an item that grants perks for an unlimited amount of time, you should use a durable SKU instead of a consumable SKU.
Learn more about consumable and durable items in the [Implementing One-Time Purchases](/developers/monetization/implementing-one-time-purchases) guide.
***
## Initiating Purchases
After displaying your SKUs in your custom storefront, you will need to initiate a purchase when a user selects a SKU to purchase.
To initiate a purchase in your activity, use the Embedded App SDK to call the [`startPurchase()`](/developers/developer-tools/embedded-app-sdk#startpurchase) command with the selected SKU `id`. This command will open the purchase flow modal in the Discord client, allowing users to purchase that SKU.
Learn more about the [`startPurchase()`](/developers/developer-tools/embedded-app-sdk#startpurchase) command in the [Embedded App SDK Reference](/developers/developer-tools/embedded-app-sdk).
To know when it has been completed, you can subscribe to `ENTITLEMENT_CREATE` events.
### Subscribing to Purchase Events with the Embedded App SDK
Once a user has completed a purchase, Discord will emit an `ENTITLEMENT_CREATE` event. You can subscribe to this event using the Embedded App SDK to know when a user has successfully purchased a SKU and is granted an entitlement.
You can subscribe to the `ENTITLEMENT_CREATE` event using the [`subscribe()`](/developers/developer-tools/embedded-app-sdk#subscribe) method in the Embedded App SDK.
Here's an example of how to subscribe to the `ENTITLEMENT_CREATE` event:
```js theme={"system"}
import {DiscordSDK} from '@discord/embedded-app-sdk';
const discordSdk = new DiscordSDK(clientId);
await discordSdk.ready();
const handleEntitlementCreate = () => {
// refetch entitlements from server using the Entitlement HTTP API endpoint
};
discordSdk.subscribe('ENTITLEMENT_CREATE', handleEntitlementCreate);
```
You can also subscribe to the `ENTITLEMENT_CREATE` event using the [Gateway API](/developers/events/gateway-events#entitlements) to receive the event in your app's backend or use the [List Entitlements](/developers/resources/entitlement#list-entitlements) HTTP API endpoint to fetch a user's entitlements.
***
## Data Security Considerations
When working with SKUs and Entitlements in an Activity, it's crucial to ensure the security and integrity of your application's data. Here are some things to keep in mind:
Developers should ensure the accuracy of data obtained via [Embedded App SDK](/developers/developer-tools/embedded-app-sdk) commands and events. A malicious actor could potentially establish their own RPC connection and interact with your application client, posing as Discord. While this might not be an issue for most SDK commands and events, it becomes critical when dealing with perks offered through In-App purchases.
If your application relies solely on SDK data to determine a user's entitlements, a malicious actor could exploit this to gain access to premium products, features, or advantages without paying. This is particularly relevant for commands like [`getEntitlements()`](/developers/developer-tools/embedded-app-sdk#getentitlements).
### Use the Discord HTTP API for Verification
Data fetched from the Discord HTTP API from your application's backend servers can be trusted and should be treated as the source of truth. This data should be used to validate any inconsistent client-side data.
### Recommended Approach
* **Optimistically** use client-side techniques such as SDK commands and events to fetch SKUs and Entitlements.
* **Verify** the results via the Discord HTTP API from your application's backend.
In summary, use the principle of "Trust (the SDK), but Verify (via the API)" to ensure the security and integrity of your application's premium features.
***
## Testing Your In-App Purchases
To test your In-App Purchases in your Activity, you will need to follow testing guidelines for both types of SKUs: [One-Time Purchases](/developers/monetization/implementing-one-time-purchases#testing-your-onetime-purchase-implementation) and [Subscriptions](/developers/monetization/implementing-app-subscriptions#testing-your-app-subscription-implementation).
After you've tested your In-App Purchase flows, verify that your application has correctly granted the user the premium perks associated with the SKUs that were purchased during testing.
***
## Next Steps
Check out our example implementation of In-App Purchase in our [SDK Playground Example Application](https://github.com/discord/embedded-app-sdk-examples/tree/main/sdk-playground).
The example implementation includes client-side and server-side code. It also follows the [security considerations](/developers/monetization/implementing-iap-for-activities#data-security-considerations) you should make when working with SKUs and entitlements.
This reference example implements the commands and events available to you within the Embedded App SDK, including In-App Purchases.
# Implementing One-Time Purchases
Source: https://docs.discord.com/developers/monetization/implementing-one-time-purchases
Guide to implementing durable and consumable one-time purchases.
One-time purchases enable you to charge your users for premium functionality with in-app items.
* Before you can add one-time purchases to your app, you must [Enable Monetization](/developers/monetization/enabling-monetization) for your app.
* Once you've confirmed eligibility for your app and team, you will be able to set up a [SKU](/developers/resources/sku) (stock-keeping unit) to represent your one-time purchases.
***
## Types of One-Time Purchases
When creating items for one-time purchase, you can choose between durable and consumable items:
* **Durable Items**: A one-time purchase that is permanent and is not subject to either renewal or consumption, such as lifetime access to an app's premium features.
* **Consumable Items**: A one-time, non-renewable purchase that provides access, such as a temporary power-up or boost in a game.
***
## How One-Time Purchases Work
#### For Durable SKUs
* When a user purchases your durable SKU, Discord creates an [Entitlement](/developers/resources/entitlement) for the purchasing user and that specific [SKU](/developers/resources/sku).
* You will receive an `ENTITLEMENT_CREATE` event via the Gateway.
* This entitlement is now available via the `LIST Entitlements` API endpoint.
* This entitlement will be available on `Interaction Payloads` initiated from the entitled user.
#### For Consumable SKUs
* When a user purchases your consumable SKU, Discord creates an [Entitlement](/developers/resources/entitlement) for the purchasing user and that specific SKU.
* You will receive an `ENTITLEMENT_CREATE` event via the Gateway.
* This entitlement is now available via the `LIST Entitlements` API endpoint.
* This entitlement will be available on `Interaction Payloads` initiated from the entitled user or users in a guild (for guild subscriptions).
* Users cannot repurchase this SKU until you consume the entitlement using the [Consume Entitlement API](/developers/resources/entitlement#consume-an-entitlement) endpoint.
* In [Test Mode](/developers/monetization/implementing-one-time-purchases#using-application-test-mode), repeated purchases are permitted without consumption for developer convenience.
* When you receive an `ENTITLEMENT_CREATE` event for a consumable SKU, you should process the item purchase in your app and consume the entitlement as soon as possible.
***
## Working with Entitlements
When a user purchases a one-time purchase SKU, an entitlement is created. [Entitlements](/developers/resources/entitlement) represent the user's access to your consumable or durable item.
Depending on your app's features, you can use a combination of [Gateway events](/developers/events/gateway-events#entitlements), the [Entitlement HTTP API](/developers/resources/entitlement), and [interaction payloads](/developers/interactions/receiving-and-responding) to keep track of entitlements to users who have purchased items in your app.
### Accessing Entitlements with Gateway Events
When a user purchases a SKU, Discord will emit an [`ENTITLEMENT_CREATE`](/developers/events/gateway-events#entitlements) event. This event will contain the entitlement object that represents the user's access to the SKU. You can use this event to keep track of the user's entitlements in near-time. For One-Time Purchases, you may also receive an `ENTITLEMENT_DELETE` event if the user's entitlement is revoked.
### Accessing Entitlements with the HTTP API
Entitlements are available via the [List Entitlements](/developers/resources/entitlement#list-entitlements) endpoint. You can filter entitlements by a specific user or set of SKUs by using the `?user_id=XYZ` or `?sku_ids=XYZ` query parameters.
### Accessing Entitlements on Interaction Payloads
Entitlements are also available on the `Interaction Payload` when a user interacts with your app. You can find the entitlements on the `entitlements` field of the `Interaction Payload` when [receiving and responding to interactions](/developers/interactions/receiving-and-responding).
### Accessing Entitlements with the Embedded App SDK
When using the [Embedded App SDK](/developers/developer-tools/embedded-app-sdk) to build an [Activity](/developers/activities/overview), you can also [access a user's entitlements](/developers/developer-tools/embedded-app-sdk#getentitlements). Check out the [Implementing In-App Purchases for Activities](/developers/monetization/implementing-iap-for-activities) guide to learn more about monetization with the Embedded App SDK.
Depending on your app's needs, you can use a combination of these methods to keep track of user entitlements.
***
## One-Time Purchase Considerations
When implementing one-time purchases, you should consider the following:
### For Durable One-Time Purchases
When offering durable items, users will have access to the SKU indefinitely. Durable items can't be consumed, so you don't need to worry about the user losing access to the item except in the case of a refund.
### For Consumable One-Time Purchases
When offering consumable items, users can only have one unconsumed entitlement at a time. To handle consumable items in your app or game, you should process and store the consumable item in your app and then make a call to the [Consume Entitlement](/developers/resources/entitlement#consume-an-entitlement) endpoint so that the user can purchase more of this item in the future.
Consuming the entitlement will update the entitlement to return a true value in the entitlement's `consumed` field. You will need to think through how your app keeps track of consumable items to decide on the best strategy for when to consume these entitlements and store the state of the consumable item and quantity in your app.
***
## Prompting Users to Purchase an Item
### Responding with a Premium Button
[Responding with a premium button](/developers/monetization/managing-skus#responding-with-a-premium-button) gives you the ability to prompt users to subscribe to your app when they attempt to use a premium feature without a subscription.
You can do this by sending a message with a [button](/developers/components/reference#button) with a [premium style](/developers/components/reference#button-button-styles) and a `sku_id` that allows the user to upgrade to your premium offering.
### Starting a Purchase from an Activity
If you are using the [Embedded App SDK](/developers/developer-tools/embedded-app-sdk) to build an [Activity](/developers/activities/overview), you can also launch the purchase flow for a specific SKU using the SDK. Check out the [Implementing In-App Purchases for Activities](/developers/monetization/implementing-iap-for-activities) guide to learn more about monetization with the Embedded App SDK.
***
## Testing Your One-Time Purchase Implementation
The method of testing purchases for One-Time Purchases differs from the method for App Subscriptions. **Do NOT use Test Entitlements for One-Time Purchases.**
### Using Application Test Mode
While in Application Test Mode, you can freely make "purchases" of One-Time Purchase SKUs tied to your application. That means you can test buying your consumable and durable items by going through the In-App Purchase flow without any credit card charges.
You still need to have a valid payment method on file to "purchase" SKUs in Application Test Mode. It just won't be charged at checkout.
To enable it, first, make sure you have a payment method on file in `User Settings -> Billing` and then:
1. Open up the Discord app
2. Click on the Settings cog in the bottom left corner
3. Go to the `Advanced` page under App Settings
4. Toggle "Developer Mode" **on** and "Application Test Mode" **on**, and enter your application ID. You can leave the other settings as-is.
5. Exit user settings
Once you enabled Application Test Mode successfully, you should see an orange bar across the top of your screen with the name of your app.
You can now navigate to your Store page and purchase your one-time purchase items without being charged.
The entitlements tied to items purchased in Application Test Mode can be identified by entitlements with a `type` value of 4 to represent `TEST_MODE_PURCHASE`.
The "Go To SKU" button does not currently work. To purchase your SKU in test mode, go to your Store page.
# Managing SKUs
Source: https://docs.discord.com/developers/monetization/managing-skus
Learn how to create and manage SKUs that represent your app's premium offerings.
The premium items and subscriptions you offer in your app are represented by SKUs. **SKU** stands for Stock Keeping Unit and is a unique identifier for your premium offerings.
[SKUs](/developers/resources/sku) are the building blocks of your premium offerings and you can manage them in the Developer Portal.
***
## Creating a SKU
To create a new SKU, navigate to your [app's settings](https://discord.com/developers/applications) and select the [Monetization -> Manage SKUs](https://discord.com/developers/applications/select/skus) tab. From there, you can create a new SKU by clicking the `Create SKU` button.
When you click on `Create SKU`, you have the option to select from the following:
* **User Subscription**: An auto-recurring subscription that grants benefits to one user in all servers
* **Guild Subscription**: An auto-recurring subscription that grants benefits to all users in one server
* **Consumable**: A one-time purchase that provides a temporary benefit, which is consumed upon use.
* **Durable**: A one-time purchase that **grants** a permanent addition or enhancement.
Once you select the SKU type, enter a name for your SKU to continue.
### Creating Subscription Tiers
You can create multiple subscription tiers to offer different benefits at different price points. Each tier can have its own set of benefits and price and is represented by unique SKUs.
To support upgrading and downgrading between subscription tiers, see our guide on [Implementing App Subscriptions](/developers/monetization/implementing-app-subscriptions#supporting-upgrades-and-downgrades).
### SKU Limitations
There are some limitations to the number of SKUs you can create:
* You can create up to 50 total SKUs per app.
* You can offer either user subscription SKUs or guild subscription SKUs, but not both simultaneously.
* SKU prices must be selected from the list of available prices.
If you need more SKUs than the 50 limit, consider creating a consumable in-app currency SKU that can be used to purchase items that are tracked in your app.
***
## Customizing Your SKUs
Once you've created a SKU, you can customize it to match your app's branding and the benefits you want to offer. You can customize:
* A name for your premium SKU, max 80 characters.
* A description for your premium SKU, max 160 characters
* An image for your premium SKU
* A price for your premium SKU
Your list of benefits will displayed on your app's Store page, the App Directory, and during the purchase and cancellation flows to explain to users the benefits of your premium offering. These benefits can have:
* Up to 6 benefits
* An emoji, standard or custom
* A name, max 80 characters
* A description, max 160 characters
### Pricing Your SKUs
When setting the price for your SKU, you can select from a list of predefined prices. The prices are automatically converted to the user's local currency based on their locale.
Subscription SKUs are automatically charged each month unless canceled. Changing the price of this SKU will only change it for new subscribers. Existing subscribers will continue to be charged the existing price.
To set an icon using a standard Unicode emoji, enter the emoji in the `Unicode Emoji or Custom Emoji Name` field.
Using an emoji keyboard can make it easier to pick an icon to display alongside your SKU benefit.
MacOS: `control + command + space bar`
Windows: `Windows + .`
To use a custom emoji, set a value for both fields:
* Name of your custom emoji
* ID of the custom emoji
You can find the ID of the emoji in the Discord app by escaping the emoji in a message with a backslash character `\`. For example, `\:uwu:` will render with the name and ID of the emoji.
***
## Publishing and Unpublishing SKUs
When you initially create a SKU, it will be in an `Unavailable` state. This SKU is not yet available for purchase by users. You can edit the SKU to add a price, benefits, and other details before publishing it.
While creating and editing SKUs in your [app's settings](https://discord.com/developers/applications) on the [Monetization -> Manage SKUs](https://discord.com/developers/applications/select/skus) tab, you have a few options for managing your SKUs visibility and publishing to your users:
* Publish SKU
* Unpublish SKU
* Delete SKU
### Publishing a SKU
When publishing a SKU, you have the option to make it **Available via the Store and API** or **Available via the API Only**.
#### Publishing to Store & API
Available to be purchased and visible in your app's store.
#### Publishing to API Only
You can only make API calls or use the Embedded App SDK to grant entitlements for this SKU.
### Unpublishing a SKU
**Danger:** Unpublishing a SKU can affect your users' existing subscriptions and entitlements
Unpublishing a SKU removes it from the Store and the API, making it unavailable for purchase.
Unpublishing a SKU has the following effects:
* For subscription SKUs, subscriptions will not be renewed for users and guilds that have this SKU at the end of the billing period.
* Users and guilds will still be entitled to the SKU until the end of the billing period.
* For consumable and durable SKUs, users will still be entitled to the SKU if they purchased it before it was unpublished.
* Does not delete a SKU.
### Deleting a SKU
**Danger:** Deleting a SKU can affect your users' existing subscriptions and entitlements
Deletes a SKU in the UI and makes it unavailable for publishing. Deleted SKUs are still listed when calling [List SKUs](/developers/resources/sku#list-skus) in the API.
Deleting a SKU has the following effects:
* For subscription SKUs, users and guilds will be immediately unsubscribed from the SKU. Their entitlement will still be valid until the end of the billing period.
* For consumable and durable SKUs, users will still be entitled to the SKU if they purchased it before it was unpublished.
***
## Editing a Published SKU
If you wish to change a SKU that is published, you can do so at any time by first unpublishing the currently published one. When you unpublish a SKU, it is no longer available for sale and users who have already subscribed will not renew at the end of their billing period. You must continue to make the premium offering available to them until the end of their subscription.
### Changing a Subscription SKU Price
When you change the price of a user or guild subscription SKU, it will only affect new subscribers. Existing subscribers will continue to be charged the price of the SKU at the time they subscribed.
***
## Integrating SKUs in Your App
After you've published your SKUs, you are ready to start implementing your premium features in your app. See our guides to get started.
}>
Learn how to start and manage recurring subscriptions within your app.
}>
Learn how to implement one-time purchases in your app.
***
## Viewing your Store Page
Users can access an app's Store page from the Bot User's profile in a server. This allows users to view an available subscription and one-time purchases, select a subscription to view its perks, benefits, and details, and make a purchase directly from an app's Store page.
Only subscriptions and items that have been published to the Store will be visible to users on the Store page.
#### Accessing your Store page from a Bot User's Profile
#### Subscriptions in Your Store page
#### Items in Your Store page
***
## Linking to a Specific SKU
You can link directly to a specific SKU using our Application Directory Store URL scheme:
`https://discord.com/application-directory/:appID/store/:skuID`
* When used in chat, it will render as a rich embed that allows users to launch a modal to view either the SKU details or checkout flow
* When used as a direct URL in a browser, it will take the user to your product in the Application Directory on web
***
## Linking To Your Store
You can link directly to your Store page using our Application Directory Store URL scheme:
`https://discord.com/application-directory/:appID/store`
* When used in chat, it will render as a rich embed that allows users to launch a modal to your Store page
* When used as a direct URL in a browser, it will take the user to your Store page in the Application Directory on web
***
## Responding with a Premium Button
You can prompt users to purchase item or subscription SKUs using a [button](/developers/components/reference#button) with a [premium style](/developers/components/reference#button-button-styles) and a `sku_id`. You can use this premium button style anywhere you would use message components, such as in a command response.
```javascript theme={"system"}
return new JsonResponse({
type: 4, // InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE
data: {
content: "This command requires Nelly Premium! Upgrade now to get access to these features!",
components: [{
type: MessageComponentTypes.ACTION_ROW,
components: [
{
type: MessageComponentTypes.BUTTON,
style: 6, // ButtonStyleTypes.PREMIUM
sku_id: '1234965026943668316',
},
],
}]
},
});
```
# Monetizing Your Discord App
Source: https://docs.discord.com/developers/monetization/overview
Learn how to add premium features to your Discord app with subscriptions and purchases.
Add subscriptions and one-time purchases to your app using Discord's built-in checkout and payment flow. For an introduction to Premium Apps and what you can offer, see the [Premium Apps & Activities](/developers/platform/app-monetization) platform page.
App Monetization applies to [Bots](/developers/bots/overview) and [Activities](/developers/activities/overview).
***
## Components of a Premium App
To integrate a premium feature into your application, there are three primary components of our Monetization API:
* [SKUs](/developers/resources/sku) represent specific items or subscription options your app offers. Each SKU is a unique offering.
* [Entitlements](/developers/resources/entitlement) indicate whether a user has access to a specific premium offering or SKU.
* [Subscriptions](/developers/resources/subscription) represent an ongoing agreement where a user commits to paying for an entitlement on a recurring basis until canceled.
### Types of SKUs
There are two types of SKUs that you can create for your app:
#### One-Time Purchase SKUs
A one-time purchase SKU represents a single item or feature that a user can purchase once. Developers can offer two types of one-time purchases:
* Durable items: Items that a user can purchase once and keep forever. For example, a user might purchase a "premium" upgrade that unlocks premium features in an app.
* Consumable items: Items that a user can purchase once and use up. For example, a user might purchase a "boost" item that gives them a temporary boost in an app.
#### Subscription SKUs
A subscription SKU represents a recurring purchase that a user can subscribe to for a set period of time. Developers can offer two types of subscriptions:
* User subscriptions: A user subscribes to a SKU for themselves. In this case, only the purchasing user is considered entitled to the SKU.
* Guild subscriptions: A user subscribes to a SKU for their guild. All members of that guild are considered entitled to the SKU.
***
## Next Steps
Ready to start monetizing your app?
Follow our full guide for [Enabling Monetization](/developers/monetization/enabling-monetization) and implementing premium features in your app.
# Account Linking
Source: https://docs.discord.com/developers/platform/account-linking
Connect a player's Discord account to their in-game account to unlock richer social experiences.
*Already familiar? Jump to the [implementation guides](#implementing-account-linking) or the [Social SDK docs](/developers/discord-social-sdk/overview).*
## What is Account Linking?
Account linking connects a player's Discord account to their account in your game. Once linked, players gain access to their Discord friends list, game invites, rich presence, and voice directly inside your game.
This link persists across sessions, so players authenticate once and won't need to re-link unless your account linking [scopes](/developers/discord-social-sdk/core-concepts/oauth2-scopes) change. Players who aren't ready to link (or don't have a Discord account yet) can still use social features through [provisional accounts](/developers/discord-social-sdk/development-guides/using-provisional-accounts). When they do link later, their friends, DM history, and lobby memberships transfer automatically to their Discord account.
Account linking is also important for [Social Commerce](/developers/social-commerce/overview) in Discord communities. Players with linked accounts can claim in-game items that they purchase or receive as gifts from Discord Game Shops.
## Why Account Linking Matters
### For Players
Account linking transforms Discord from a second-screen utility into a personalized gaming companion:
* **Seamless social experiences:** Access Discord friends and game invites directly within games.
* **Rich presence integration:** Show detailed game status and activity to friends.
* **Social Commerce:** Claim in-game items purchased or gifted from Discord Game Shops.
### For Developers
Linked players are demonstrably more valuable to your game:
* Players who link in-game accounts to Discord via Social SDK **play more often** - median game launch days increase is 25%\*
* Players who link in-game accounts to Discord via the Social SDK **play longer** - median session length increase is 16%\*
* Players who link in-game accounts to Discord via the Social SDK **retain better** - median D28 retention is 34%\*
These aren't projections. This is causal data from games that have implemented account linking.
\**Discord Internal Data, 2025*
## How Account Linking Works
### Understanding the OAuth2 Flow
Account linking is built on OAuth2, an industry-standard authorization protocol. Here's what happens under the hood when a player links their account:
1. **Your game asks Discord to start the OAuth flow.**
2. **Discord shows the player an OAuth modal asking them to authorize your game and the OAuth scopes being requested.**
3. **The player approves** (authorizes).
4. **Discord gives your game a temporary access token**, which is proof the player agreed to authorize.
5. **Your game uses that token** to access Discord social features on the player's behalf.
You don't need to implement OAuth2 from scratch. The Discord Social SDK handles the authorization flow, token management, and API communication for you. Web Flow uses standard OAuth2, which most web frameworks support out of the box.
The access token your game receives is scoped, meaning it can only access the Discord features you've requested. The Social SDK provides two default scope sets: `GetDefaultPresenceScopes` for presence and friends, and `GetDefaultCommunicationScopes` for messaging, lobbies, and voice.
See the [OAuth2 Scopes for Social SDK page](/developers/discord-social-sdk/core-concepts/oauth2-scopes) for full details.
### Two Ways to Link
You can implement account linking through two flows:
| | **Game Flow** | **Web Flow** |
| --------------------- | ---------------------------------------------------------------------------------------- | -------------------------------------------------- |
| **What it is** | Player authorizes from inside your game via the Discord client, overlay, or browser | Player authorizes through a web page on any device |
| **Requires** | Discord Social SDK | Standard OAuth2 implementation |
| **Platforms** | Desktop, Mobile, Console | All platforms that support web |
| **Player experience** | Seamless, stays in-game context (Discord app/overlay launches, or falls back to browser) | Opens a browser / web page |
Both flows produce the same result: the player's Discord account is linked to your game. Neither flow is better than the other, and as long as the same OAuth2 scopes are used, the player gets the same features regardless of which flow they used to link.
We recommend offering both flows to give players the most flexibility. Game Flow only works when the player is actively playing your game on the same device, while Web Flow is a more portable option that supports account linking when the player is engaging with your game through your Official Game Community, Game Shop, or any other web-based touchpoint.
Once a player links through either method, they're linked for both. You can start with one flow and add the other later, and players won't need to re-link.
Web Flow does not mean launching a web browser inside your game. Web Flow is a standalone web-based OAuth2 flow, typically used from Discord entry points or external URLs.
## Implementing Account Linking
### Linking from Your Game
Use the Discord Social SDK to let players link their accounts directly from your game. Choose the guide for your platform:
Guide on implementing account linking from your desktop game using the Social SDK.
Guide on implementing account linking from your mobile game using the Social SDK.
Guide on implementing account linking from your console game using the Social SDK.
### Linking from the Web
Use a standard OAuth2 flow to let players link their accounts through a web page on any device. This doesn't require the Social SDK and works well if you already have web infrastructure. Web Flow is the primary method for enabling [Social Commerce](/developers/social-commerce/overview) features like Game Shops and Game Stat Widgets.
Guide on implementing the standard OAuth2 web flow for account linking.
### Linking from Discord Entry Points
Entry points are buttons and prompts Discord displays throughout the Discord client encouraging players to link their account with your game, even when the game isn't running. Accounts linked from Discord work exactly the same as those linked from your game.
When a player uses a Discord entry point, Discord determines which flow to use:
1. **Game Flow** is used if the game is running and the developer has called the `RegisterAuthorizeRequestCallback` function in the Social SDK
2. **Web Flow** is used if a **Connection Entrypoint URL** is configured in the Developer Portal
Some of the current Discord entry points include:
**Game Detection**
Discord detects the player is playing your game and provides an entry point to link if they haven't already.
**Game Invites**
Players are optionally able to link when receiving an invite for your game, which helps them get set up and playing with their friends faster.
**Claiming Game Shop Items**
Players need to account link when redeeming items from your Game Shop to be able to receive their in-game rewards.
Configuring Web Flow to work from official Discord entry points is currently available to select partners. Contact Discord if you're interested in enabling this for your game.
Guide on enabling Discord entry points so players can link from within the Discord client.
## Provisional Accounts
Players who don't have Discord, or who choose not to link yet, can still use social features through provisional accounts. They can add friends, join lobbies, send messages, and participate in voice chat without a Discord account.
When they later link a Discord account, their friends, DM history, and lobby memberships transfer automatically. Learn more in the [Provisional Accounts guide](/developers/discord-social-sdk/development-guides/using-provisional-accounts).
## Next Steps
Discover the Discord-powered social features that account linking unlocks for your game.
Best practices for designing the account linking experience in your game.
Step-by-step guides for integrating the Discord Social SDK with C++, Unity, and Unreal Engine.
# Activities
Source: https://docs.discord.com/developers/platform/activities
Build multiplayer games and social experiences that run directly inside Discord.
*Already familiar? Jump to the [Activities docs](/developers/activities/overview) or [Building Your First Activity](/developers/activities/building-an-activity).*
## Multiplayer Experiences in Discord
Activities are multiplayer games and social experiences embedded directly in Discord. They can be launched in channels, DMs, or from the App Launcher with no external window or separate download required.
Players can jump in together with friends already in a voice channel, making Activities a natural fit for party games, co-watching, collaborative tools, and more.
## Embedded App SDK for Activities
Under the hood, Activities are an iframe that communicates with Discord using the [Embedded App SDK](/developers/developer-tools/embedded-app-sdk). The SDK provides methods for interacting between your Activity and the Discord client.
## Next Steps
}>
Full guide to how Activities work, launch flows, and the Embedded App SDK.
}>
Deep dive into the Activity lifecycle, iframe environment, and client communication.
}>
Step-by-step guide to setting up and deploying your first Activity.
# Premium Apps & Activities
Source: https://docs.discord.com/developers/platform/app-monetization
Add subscriptions and one-time purchases to your Discord app with Premium Apps.
App Monetization applies to [Bots](/developers/bots/overview) and [Activities](/developers/activities/overview). If you're building a game integration, check out [Social Commerce & Game Shops](/developers/social-commerce/overview) for more information on how to monetize your game with Discord.
Our Premium Apps features let you charge for your app's features directly within Discord, using Discord's built-in payment and checkout flow. Users never leave the platform to subscribe or make a purchase, which reduces friction and keeps the experience native. Discord handles billing, fraud detection, and receipts, while you handle granting access to your premium features.
You can offer two types of monetization:
* **Recurring monthly subscriptions** with the option to offer to individuals or whole servers
* **One-Time purchases** with the option to offer durable upgrades or consumable items
Both types integrate through the same [SKU](/developers/resources/sku) and [Entitlement](/developers/resources/entitlement) APIs, so your app checks whether a user is entitled to a feature before granting access.
Monetized apps also get additional surface area on the App Directory, including a store page where users can browse and purchase your offerings without having to install your app first.
Full guide to SKUs, Entitlements, Subscriptions, and the App Directory store page.
Step-by-step setup for enabling payments and creating your first SKU.
# Bots & Companion Apps
Source: https://docs.discord.com/developers/platform/bots
Build bots and companion apps that connect to Discord and interact with servers, members, and events.
*Already familiar? Jump to the [Bots & Companion Apps docs](/developers/bots/overview) or [Getting Started guide](/developers/quick-start/getting-started).*
Bots are the most common type of Discord app. They appear in servers as bot users with an `APP` tag and can listen to events, respond to slash commands, moderate servers, send messages, and much more, all powered by your code via the Discord API.
Bots can connect to Discord in two ways: a persistent [Gateway WebSocket](/developers/events/gateway) for real-time events, or an [HTTP interactions endpoint](/developers/interactions/overview) for slash commands and UI components without a persistent connection. Most bots use one or both depending on their use case.
Common bot use cases include moderation tools, server utilities, games, integrations with external services, and automated workflows.
If you only need to push messages into a channel, a [webhook](/developers/platform/webhooks) might be a better fit than a full bot. Webhooks are simpler to set up and can send messages with rich embeds, but they can't listen to events or respond to interactions like bots can.
}>
What bots are, how they work, and when to use one.
}>
Build and run your first Discord bot end to end.
}>
Guides on interactions, components, monetization, and more.
# Claim Your Game
Source: https://docs.discord.com/developers/platform/claim-your-game
Learn how to claim your game on Discord to unlock a verified checkmark and take control of your game's profile.
## What is Game Claiming?
Claiming your game connects your Discord community server and game profile to your verified developer identity. Players get a verified place to find the official game community, and you get direct control over how your game is represented on the Discord app.
## Benefits of Claiming Your Game
### Game Profile Customization
Discord automatically generates a [game profile](/developers/platform/game-profiles) for every game it detects. Once you claim your game, you control the information and visual appearance of that profile across four sections:
* **Title:** description, genres, platforms
* **Visual Assets:** cover, icon, banner, screenshots
* **Links:** official website, social media
* **Company:** name, publisher or developer role
Changes are made directly in the [Developer Portal](https://discord.com/developers/applications), making it easy to keep your game's identity current with live service updates or seasonal content.
### Verified Checkmark
Once your claim is confirmed, your Discord server receives a green verified badge on its server profile, marking it as the verified community for your game on Discord. The checkmark is visible in Server Discovery and is only available to servers associated with a claimed game. [Learn more about verified servers](https://discord.com/community).
***
## Before You Begin
Make sure the following are in place before you start:
* Your game must have a Steam store page. Other storefronts are not yet supported.
* Your Steam store page must have a publicly visible Discord invite link. During the claim process, you'll receive a `proof` value to append to that link before the claim can be submitted.
* Games whose primary purpose is the delivery of sexual content or are Mods of other games are not eligible for claiming.
* Your Discord application must be assigned to a [development team](/developers/topics/teams#teams), not owned by an individual account.
* The server owner must be a member of your Team. Discord verifies ownership by sending a verification code to the server owner's email address.
* Discord must have an official record of your game. Discord automatically recognizes games that have established a sufficient playerbase and play time among Discord users over an extended period.
To check whether Discord has a record of your game, search for it in the Game Identity dropdown in [Step 2](#step-2-find-and-select-your-game).
If your game does not appear there, Discord does not yet have an official
record of it. As your game's playerbase and playtime grow, it will become eligible for automatic recognition.
Note that implementing [Rich Presence](/developers/platform/rich-presence) via the Discord Social SDK is separate from automatic game detection as these are independent systems, and having Rich Presence alone does not make a game claimable.
At this time, games in private beta or limited release are not supported. Your game must be publicly available and playable to be eligible for claiming.
***
## Claiming Your Game
### Step 1: Open Game Identity in the Developer Portal
Visit [Game Identity in the Developer Portal](https://discord.com/developers/applications/select/game-identity) and select your application. The game claiming interface will appear automatically.
If you're an existing SDK partner, claim your game to the application you're already using for your SDK integration.
### Step 2: Find and Select Your Game
Search for your game by name in the Game Identity interface and select it to begin the claim.
If your game does not appear there, Discord does not yet have an official record of it. As such, you will not be able to claim your game at this time. As your game’s playerbase and playtime grow, it will become eligible for automatic recognition.
### Step 3: Update Your Steam Invite Link
After selecting your game, the claim interface will display a `proof` query parameter. Copy this value and append it to your Discord invite link on your Steam store page.
1. Copy the `proof` value shown in the Developer Portal.
2. Update the Discord invite link on your Steam store page to include it (e.g., `discord.gg/yourcode?proof=`).
Once your link is updated, Discord will verify the `proof` value before you can continue.
### Step 4: Verify Server Ownership
To confirm you own the Discord server linked on your Steam store page, Discord will send a **verification code to the server owner's email address**.
1. Check the email inbox associated with the owning Discord account.
2. Enter the code when prompted to continue.
### Step 5: Provide Your Details
You'll be asked to provide:
* Your full legal name (required)
* A professional contact email (required)
* Your role at the company (optional)
* Your company or game website (optional)
This information will be used as described in our [Developer Terms of Service](https://support-dev.discord.com/hc/en-us/articles/8562894815383-Discord-Developer-Terms-of-Service).
### Step 6: Submit for Review
Once all steps are complete, submit your claim. Discord will notify you if the claim is successful, or reach out if additional information is needed.
***
## If Your Claim is Successful
When your claim is successful, you'll be notified by email (🎉) and the **Game Identity** section in your **Developer Portal** will update. You can then begin customizing your game's profile.
## If Your Claim is Rejected
If your claim is rejected, please work within the [Developer Support](https://dis.gd/developer-support/game-development) ticket to address any concerns.
***
## Current Limitations
Game Claiming currently has the following limitations:
* **No mobile support:** Games must be playable on non-mobile platforms (PC, Mac, or consoles). Mobile-only games cannot currently be claimed.
* **Steam only:** Only games with a Steam store page can currently be claimed. Support for other platforms is not yet available.
* **Released only:** Only games listed on Steam and playable by the public (no private betas) can currently be claimed.
We're actively working on expanding coverage so more games can be claimed over time.
If you believe your game should be listed and you cannot find your game, please [contact us](https://dis.gd/developer-support/game-development).
***
## Next Steps
}>
Build social features directly into your game — friends, voice, lobbies, and more.
}>
Learn how Discord surfaces your game's profile and where it appears across the platform.
# Community Servers
Source: https://docs.discord.com/developers/platform/community-servers
Build and grow a player community around your game by building out a community server on Discord.
Community Servers are Discord servers with a dedicated set of features for building a public audience:
* [Structured Community Onboarding](https://support.discord.com/hc/en-us/articles/10394859532823-Community-Onboarding-Examples)
* [Announcement Channels](https://support.discord.com/hc/en-us/articles/360032008192-Announcement-Channel-FAQ)
* [AutoMod](https://support.discord.com/hc/en-us/articles/4421269296535-AutoMod-FAQ)
* [Server Discovery](https://support.discord.com/hc/en-us/articles/360023968311-Server-Discovery)
* [Server Insights](https://support.discord.com/hc/en-us/articles/360032807371-Server-Insights-FAQ)
Most game developers use a Community Server as the official home for their players as a place to share updates, offer support, and keep the community connected between game sessions.
## Enabling Community Server Features
Any server can become a Community Server by following the steps in [Enabling Your Community Server](https://support.discord.com/hc/en-us/articles/360047132851-Enabling-Your-Community-Server). Once you enable Community features, you can start building out your server with the features listed above.
## Official Game Communities
Once you have a Community Server set up, you can claim it as your Official Game Community by [Claiming Your Game](/developers/platform/claim-your-game). This gives you access to features like a custom vanity URL, a verified badge, and the ability to link your game to your server so players can easily find it from your game's store page and Discord profile.
## Next Steps
Step-by-step instructions for enabling Community features on your Discord server.
Set up a community server, design roles and channels, and bring players in from your game
Guides for building and growing your Discord community.
# Components & Modals
Source: https://docs.discord.com/developers/platform/components
Add interactive buttons, menus, layout elements, and modal dialogs to your Discord app.
Discord's component system lets your app build rich, interactive UI directly inside the Discord client, with no custom frontend needed.
## Message Components
Message Components are interactive and layout elements attached to messages your app sends. Discord supports three categories:
* **Layout components** for organizing content
* **Content components** for displaying text and media
* **Interactive components** like buttons, select menus, and text inputs that users can act on
Components are sent as structured data alongside your message and rendered natively in the Discord client.
## Modals
Modals are form-like overlays that your app can present to users in response to an interaction, such as a button click, slash command, or select menu selection. They collect freeform input through text fields and are a natural fit for multi-field data entry, confirmations, bug reports, or any configuration flow that needs more than a single tap to complete.
Unlike message components, modals aren't attached to a message. They're sent as an interaction response and appear as a focused overlay in the client. The user fills in the fields and submits, which triggers a new interaction your app can handle.
## Further Reading
Full guide to available component types and how components are sent in messages.
A guide on sending Message Components with code examples.
A guide on building and displaying Modal Components with code examples.
# App Discovery
Source: https://docs.discord.com/developers/platform/discovery
Make your Discord app discoverable through the App Directory, App Launcher, and social sharing.
App Discovery gives users multiple ways to find and install your app across Discord.
## App Directory
The [App Directory](https://discord.com/discovery/applications) is a searchable hub where users can browse apps by name, category, or collection, and view a full product page with descriptions, screenshots, and links.
To appear here, your app needs to be verified and opted into discovery. See [Enabling Discovery](/developers/discovery/enabling-discovery) for the step-by-step setup.
## App Launcher
The [App Launcher](https://support-apps.discord.com/hc/en-us/articles/26592957841303-How-to-Discover-and-Add-Apps#h_01J8JE2VM7QEKH9XKNR696P56C) is accessible from the app shapes icon throughout Discord and surfaces recently used apps and apps curated by collection.
## Organic Discovery & Sharing
Once your app is discoverable, social interactions within Discord become a natural acquisition channel. When users interact with your app in a server, others in that server can see it and visit your app's profile to install it themselves.
Each Discord app also has sharable links that take users to your app's profile or [store page](/developers/monetization/managing-skus#linking-to-your-store), shareable anywhere from Discord messages to external websites.
## Next Steps
Full guide to discovery surfaces, the App Directory, App Launcher, and social discovery.
Enable discovery for your app to make it available in the App Directory and App Launcher.
Learn how to make your app stand out and drive more users to discover it.
# Game Profiles
Source: https://docs.discord.com/developers/platform/game-profiles
Learn how Discord surfaces game information to users and where your game's profile appears across the platform.
Discord generates a profile for games it detects users playing. Each game profile is a dedicated page with the game's description, screenshots, and social media links. Game profiles allow users to learn more about what friends are playing or discover their next game.
## Where Game Profiles Appear
Discord surfaces game profiles in several places throughout the platform for users to discover and learn about games.
### Rich Presence
When a user is actively playing a game, their current activity appears on their profile and in the member list. Clicking the activity, then the game's cover art, opens the game's profile page.
### Game Collections in User Profiles
Discord users can add widgets to their profile, curating lists of games that appear on their profile. Clicking a game's cover art in any widget opens the game's profile page.
### Activity
The **Activity** tab on a user's profile shows games they have played recently. Clicking a game's cover art opens the game's profile page.
### Game Profile Page
The full game profile page surfaces information across four areas:
* **Title:** description, genres, platforms
* **Visual Assets:** cover, icon, banner, screenshots
* **Links:** official website, social media
* **Company:** name, publisher or developer role
***
## Take Control of Your Game Profile
If you're a game developer or publisher, you can take ownership of your game's profile on Discord. Claiming your game lets you:
* Update your game description
* Curate your screenshots with your best visuals
* Ensure your publisher and developer names are accurate
* Point players directly to your official Discord community and socials
* Get an official game community checkmark on your Discord server
Changes you make go live immediately without additional review, so you can keep your profile current with new content updates or seasonal events.
}>
Learn how to claim your game on Discord and customize your game's profile.
# Interactions & Commands
Source: https://docs.discord.com/developers/platform/interactions
Build interactive slash commands, buttons, modals, and more using HTTP or the Gateway.
Interactions are the foundation of user-facing Discord app features. When a user invokes a slash command, clicks a button, selects from a dropdown, or submits a modal, Discord sends your app an interaction payload that your app can respond to directly.
Your app can receive interactions over the Gateway or via HTTP requests to a dedicated endpoint. Both models are designed to be easy to implement and scale with your app's needs, whether you're building a simple command handler or a complex interactive experience.
## Application Commands
[Application Commands](/developers/interactions/application-commands) are the primary way users invoke your app in Discord. There are four types:
* **Slash commands:** accessed by typing `/` in the chat input
* **Message commands:** accessed from the context menu on a message
* **User commands:** accessed from the context menu on a user profile
* **Entry Point commands:** used to launch [Activities](/developers/platform/activities) from the App Launcher
When a user invokes a command, Discord sends your app an interaction payload you can respond to with a message, a modal, or a deferred response for longer-running work.
## Message Components & Modals
[Message components](/developers/platform/components) also generate interactions when users act on them. When a user clicks a button or submits a select menu attached to one of your app's messages, Discord delivers an interaction the same way it would for a command.
[Modals](/developers/platform/components#modals) work the same way. Your app responds to an interaction (a command or component click) by opening a modal, and the user's submission arrives as another interaction your app handles.
## Gateway Interactions
By default, your app receives interactions over the [Gateway](/developers/events/gateway), the same persistent WebSocket connection used for all other real-time events. If your bot is already connected to the Gateway to listen for messages, member joins, or other activity, interactions arrive through the same connection.
## HTTP Interactions
Alternatively, you can configure an **Interactions Endpoint URL** in your app's settings to receive interactions as HTTP POST requests sent directly to your server. Discord handles delivery and your app needs to respond within a few seconds.
This model works well for apps that only need to respond to commands and UI components. No persistent connection is required and it scales naturally with standard web infrastructure. To use it, your endpoint must validate Discord's request signatures and handle the initial `PING` handshake. See the [Interactions Overview](/developers/interactions/overview#preparing-for-interactions) for setup details.
## Further Reading
Full guide to interaction types, endpoint setup, and security validation.
Create slash commands, message commands, user commands, and entry point commands.
Technical reference for handling interaction payloads and sending responses.
# OAuth2 and Permissions
Source: https://docs.discord.com/developers/platform/oauth2-and-permissions
How OAuth2, scopes, and permissions work across the Discord platform.
Discord uses [OAuth2](/developers/topics/oauth2) as the standard authorization framework for granting apps access to users and servers. Understanding OAuth2 and the permissions model is essential for any Discord app.
## Two Ways to Authenticate
### Bot Token
When you create a bot user in the Developer Portal, Discord generates a **bot token**. Your code uses this token to authenticate as the bot user, a dedicated application account separate from a regular user account.
**Bot tokens:**
* Authenticate as the bot user, not on behalf of any person
* Are used for Gateway connections and most REST API calls
* Grant the permissions the bot was given when added to a server
* Should be treated like passwords and never exposed publicly
### OAuth2 User Token
When you need to act on behalf of a user, you use OAuth2 to get a user access token. The user logs in with Discord and authorizes your app for specific scopes.
**User tokens:**
* Let your app read or write data on behalf of the user as authorized by requested scopes
* Are scoped, so you only get access to what the user grants
* Are short-lived and must be refreshed
* Should be treated like passwords and never exposed publicly
## Scopes
Scopes define what your app is allowed to do. They are requested during the OAuth2 authorization flow and must be declared in the Developer Portal.
Common scopes include:
| Scope | What It Grants |
| ----------------------- | ---------------------------------------------------------- |
| `bot` | Adds your bot to a guild |
| `identify` | Read the user's basic profile (e.g., id, username, avatar) |
| `guilds` | List the guilds the user belongs to |
| `guilds.join` | Add the user to a guild |
| `email` | Read the user's email address |
| `connections` | View the user's linked accounts (Twitch, Steam, etc.) |
| `applications.commands` | Register slash commands in a guild |
For a complete list, see the [OAuth2 scopes reference](/developers/topics/oauth2#shared-resources).
## Permissions
Permissions control what a **bot** can do in a specific server or channel. When a bot is added to a server via OAuth2, the server admin grants it a set of permissions.
Permissions are stored as a bitfield. They can be:
* **Guild-level:** Apply across the entire server
* **Channel-level:** Overrides that apply to specific channels
Your app should request only the permissions it needs. Requesting excessive permissions reduces trust with users. Your app should request only the permissions it needs as described in our [Developer Policy](https://support-dev.discord.com/hc/en-us/articles/8563934450327-Discord-Developer-Policy).
## Further Reading
Full OAuth2 flow documentation, scopes, and endpoint details.
Complete permissions bitfield reference and how overrides work.
# Rich Presence
Source: https://docs.discord.com/developers/platform/rich-presence
Display real-time game activity on Discord user profiles with rich presence.
Rich Presence lets your game display live, actionable data on a Discord user's profile when they have activity sharing enabled: what they're playing, what level they're on, how long they've been in a session, or a prompt for friends to join. It's a lightweight integration that connects your app to Discord's social layer.
The data you surface is entirely up to you. Rich Presence supports custom text fields, timestamps, party size indicators, and uploaded art assets. When configured with a "Join" button, friends can jump directly into a user's game session from the profile card.
Rich Presence is available through two SDKs depending on what you're building: the Discord Social SDK for native game integrations, or the Embedded App SDK for Activities running inside Discord.
## Choosing an SDK
There are two options when integrating Rich Presence. Depending on what you're building, you'll want to choose the right SDK for your project:
* The **[Discord Social SDK](/developers/discord-social-sdk/overview)** allows you to build social features into your game or app, including friends lists, game invites, and more.
* The **[Embedded App SDK](/developers/developer-tools/embedded-app-sdk)** should be used if you're building an [Activity](/developers/activities/overview) in Discord.
Rich Presence data appears publicly on your Discord profile, so during development you should use a test account that only belongs to your private development server(s).
### Discord Social SDK
The Discord Social SDK is used for building social features directly into your game. When a user is playing your game, you can show what they're up to in their Discord profile and even prompt their friends to join in with game invites.
When integrating Rich Presence with an off-platform game, data can be shown about what a user is up to in your game. A "Join" button can also be configured to allow a user's friends to jump into their game and enable sending game invites.
[Learn how to implement Rich Presence with the Discord Social SDK](/developers/discord-social-sdk/development-guides/setting-rich-presence)
### Embedded App SDK
The Embedded App SDK is used to build Activities, which are multiplayer games and social experiences hosted in an iframe within Discord. The SDK handles communication between Discord and the Activity and helps integrate platform features (like Rich Presence!).
After a user joins an Activity, Rich Presence can be used to dynamically show data about what that user is doing or playing, as well as prompt others to join in and play along.
[Learn how to implement Rich Presence with the Embedded App SDK](/developers/rich-presence/using-with-the-embedded-app-sdk)
## Start Building
Guide on using Rich Presence in the Embedded App SDK while you're building an Activity
Guide on using Rich Presence in the Discord Social SDK for your game
Design tips and a launch checklist for shipping a polished Rich Presence integration.
# Server and Channel Management
Source: https://docs.discord.com/developers/platform/server-and-channel-management
What Discord apps can do with servers and channels, and what permissions they need.
Discord apps can programmatically interact with Discord servers (guilds) and channels. This includes reading content, posting messages, managing members, creating channels, and more. Nearly all of these actions require the bot to have the appropriate permissions in the target server or channel.
## What Apps Can Do
Some examples of server and channel management features include the ability to do the following. Check out the [API Reference](/developers/reference) for a complete list of endpoints and capabilities.
### Guilds
* Read guild metadata: name, icon, member count, features
* List and manage roles (create, update, delete, assign to members)
* Kick and ban members
* Manage and audit server events
* List guild integrations and webhooks
### Channels
* List and read channels across the server
* Create, edit, and delete channels and threads
* Set channel permissions and position
* Read and send messages (subject to message permissions)
* Manage pinned messages
### Members
* Fetch member profiles and role assignments
* Add or remove roles from members
* Modify member nicknames
* Manage member timeouts
## Key Permissions
Most server and channel operations require explicit permissions. Check out our [Permissions](/developers/topics/permissions) reference for a full list of permissions. Common ones include:
| Permission | What It Enables |
| ----------------- | ---------------------------------------------------- |
| `MANAGE_GUILD` | Edit server settings, view audit log |
| `MANAGE_CHANNELS` | Create, edit, delete channels |
| `MANAGE_ROLES` | Create and manage roles below the bot's highest role |
| `KICK_MEMBERS` | Remove members from the server |
| `BAN_MEMBERS` | Permanently ban members |
| `MANAGE_MESSAGES` | Delete messages from other users, pin messages |
| `VIEW_CHANNEL` | See a channel and its message history |
| `SEND_MESSAGES` | Post messages in a channel |
Permissions can be overridden at the channel level, so a bot may have server-wide permissions but be restricted in specific channels (or vice versa).
## Further Reading
Full API reference for guild endpoints and objects.
Full API reference for channel endpoints, message sending, and threads.
# Social Layer for Games
Source: https://docs.discord.com/developers/platform/social-layer
Add Discord-powered social features directly into your game using the Discord Social SDK.
*Already familiar? Jump to the [Discord Social SDK docs](/developers/discord-social-sdk/overview) or [Getting Started guides](/developers/discord-social-sdk/getting-started).*
Our Social Layer for Games is a set of Discord-powered features that game developers can add to their games using the [Discord Social SDK](/developers/discord-social-sdk/overview). You can integrate individual features alongside your existing social systems or build out a Discord-powered social experience.
The SDK is available for C++, Unity, and Unreal Engine, and works across PC, mobile, and console platforms.
## Account Linking
Players can [link their Discord account](/developers/platform/account-linking) to your game to unlock the full social experience. Once linked, they get access to their Discord friends list, game invites, rich presence, and voice directly inside your game. The link persists across sessions, so players authenticate once and never need to re-link.
Account linking supports both a Game Flow (from inside your game via the Social SDK) and a Web Flow (through a standard OAuth2 page), giving players flexibility in how they connect.
## Provisional Accounts
Players who prefer not to link a Discord account can still use the SDK's social features through [provisional accounts](/developers/discord-social-sdk/development-guides/using-provisional-accounts). This lets you ship a consistent social experience for your full player base, regardless of whether they have or want a Discord account.
## Friends List
The SDK provides a friends list that works for all players, whether or not they've linked a Discord account. Players using provisional accounts can add and manage in-game friends, while players who have linked their Discord account also see their Discord friends alongside any in-game friends.
## Rich Presence
[Rich Presence](/developers/platform/rich-presence) surfaces what a player is doing in your game directly on their Discord profile when they have activity sharing enabled. Friends browsing Discord can see the game, the mode, the map, and how many slots are open in a party, and can request to join with one click. This drives organic discovery and gives other players a direct path to join in.
## Game Invites
Players can invite Discord friends to join their game session with a single click. Invites appear in Discord and link directly back into your game, making it easy to fill a party or share a session.
## Direct Messages
Players can send direct messages to their Discord friends from inside your game. This keeps communication in context without pulling players out of the experience.
## Lobbies and Voice Chat
The SDK provides lobby-based voice and text chat that your game can create and manage programmatically. Players can talk to each other in a game session, a party, or any grouping your game defines, using Discord's voice infrastructure without requiring them to have the Discord app open separately.
## Further Reading
Full introduction to the SDK, feature set, and where to start.
How the SDK works, key features, and platform compatibility.
Step-by-step guides for C++, Unity, and Unreal Engine.
# Webhooks
Source: https://docs.discord.com/developers/platform/webhooks
Send messages into Discord channels or receive event notifications from Discord delivered to your app over HTTP.
Discord supports two distinct types of webhooks that work in opposite directions: **incoming webhooks** for posting messages into Discord, and **webhook events** for receiving notifications from Discord when something happens in your app.
## Incoming Webhooks
Incoming webhooks are HTTP endpoints tied to a specific Discord channel. You POST a payload to the URL and the message appears in that channel. No bot or persistent connection required. See the [Webhook Resource](/developers/resources/webhook) for the full API reference.
This makes them ideal for one-way integrations where an external system needs to push data into Discord:
* CI/CD pipelines posting build results or deployment notifications
* Monitoring systems alerting on errors or anomalies
* External apps posting updates (GitHub commits, Jira tickets, form submissions)
* Scheduled digests or reports from cron jobs
If you need your app to also listen to events or respond to users, [building a Discord bot](/developers/platform/bots) is the better fit.
## Webhook Events
Webhook events work in the opposite direction. Discord POSTs to a public URL on your server when specific events occur in your app. Instead of your app polling the API, Discord pushes the notification to you.
You subscribe to the specific event types you care about through the Developer Portal, and Discord delivers them as signed HTTP POST requests to your endpoint. Your endpoint must validate Discord's request signatures and acknowledge events promptly.
Events you can subscribe to include:
* Application authorized or de-authorized by a user
* Entitlement created (a user purchased something from your app)
* Quest enrollment and completion events
For the full list of subscribable events and setup instructions, see the [Webhook Events](/developers/events/webhook-events) documentation.
## Further Reading
}>
Full API reference for incoming webhook endpoints and objects.
}>
Subscribable event types and how to set up your endpoint to receive them.
# Developer Policy
Source: https://docs.discord.com/developers/policies/developer-policy
# Developer Terms of Service
Source: https://docs.discord.com/developers/policies/developer-terms-of-service
# Building your first Discord Bot
Source: https://docs.discord.com/developers/quick-start/getting-started
Step-by-step tutorial for building your first Discord app.
[Discord apps](/developers/quick-start/overview-of-apps) let you customize and extend Discord using a collection of APIs and interactive features. This guide will walk you through building your first Discord app using JavaScript and by the end you'll have an app that uses slash commands, sends messages, and responds to component interactions.
If you're interested in building a game or social experience in an iframe, you can follow the tutorial for [building an Activity](/developers/activities/building-an-activity)
We'll be building a Discord app that lets users play rock-paper-scissors (with 7 choices instead of 3). This guide is beginner-focused, but it assumes a basic understanding of [JavaScript](https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/JavaScript_basics).
Here's what the finished app will look like:
To make the user flow a bit more explicit:
1. User A initiates a new game and picks their object using the app's `/challenge` slash command
2. A message is sent to channel with a button inviting others to accept the challenge
3. User B presses the **Accept** button
4. User B is sent an ephemeral message where they select their object of choice
5. The result of the game is posted back into the original channel for all to see
* **[GitHub repository](https://github.com/discord/discord-example-app)** where the code from this guide lives along with some additional feature-specific code examples.
* **[discord-interactions](https://github.com/discord/discord-interactions-js)**, a library that provides types and helper functions for Discord apps.
* **[Express](https://expressjs.com)**, a popular JavaScript web framework we'll use to create a server where Discord can send us requests.
* **[ngrok](https://ngrok.com/)**, a tool that lets us tunnel our local server to a public URL where Discord can send requests.
***
## Step 0: Project Setup
Before we get started, you'll need to set up your local environment and get the project code from the [sample app repository](https://github.com/discord/discord-example-app).
We'll be developing our app locally with a little help from [ngrok](https://ngrok.com/), but you can use your preferred development environment.
If you don't have [NodeJS](https://nodejs.org/en/download/) installed, install that first.
After NodeJS is installed, open your command line and clone the project code:
```bash theme={"system"}
git clone https://github.com/discord/discord-example-app.git
```
Then navigate to the directory and install the project's dependencies:
```bash theme={"system"}
# navigate to directory
cd discord-example-app
# install dependencies
npm install
```
```
├── examples -> short, feature-specific sample apps
├──── app.js -> finished app.js code
├──── button.js
├──── command.js
├──── modal.js
├──── selectMenu.js
├── .env -> your credentials and IDs
├── app.js -> main entrypoint for app
├── commands.js -> slash command payloads + helpers
├── game.js -> logic specific to Rock, Paper, Scissors
├── utils.js -> utility functions and constants
├── package.json
├── README.md
└── .gitignore
```
With that out of the way, open your new project in the code editor of your choice, and we'll move ahead to setting up your Discord app.
## Step 1: Creating an app
First, you'll need to create an app in the developer portal if you don't have one already:
Create App
Enter a name for your app, then press **Create**.
After you create your app, you'll land on the **General Information** page of the app's settings where you can update basic information about your app like its description and icon. You'll also see an **Application ID** and **Interactions Endpoint URL**, which we'll use a bit later in the guide.
### Fetching your credentials
We'll need to set up and fetch a few sensitive values for your app, like its token and ID.
Your token is used to authorize API requests and carry your app's permissions, so they are *highly* sensitive. Make sure to never share your token or check it into any kind of version control.
Back in your project folder, rename the `.env.sample` file to `.env`. This is where we'll store all of your app's credentials.
We'll need three values from your app's settings for your `.env` file:
* On the [**General Information** page](https://discord.com/developers/applications/select/information), copy the value for **Application ID**. In `.env`, replace `` with the ID you copied.
* Back on the [**General Information** page](https://discord.com/developers/applications/select/information), copy the value for **Public Key**, which is used to ensure HTTP requests are coming from Discord. In `.env`, replace `` with the value you copied.
* On the [**Bot** page](https://discord.com/developers/applications/select/bot) under **Token**, click "Reset Token" to generate a new bot token. In `.env`, replace `` with your new token.
You won't be able to view your token again unless you regenerate it, so make sure to keep it somewhere safe (like in a password manager).
Now that you have the credentials you need, lets configure your bot user and installation settings.
### Configuring your bot
Newly-created apps have a bot user enabled by default. Bot users allow your app to appear and behave similarly to other server members when it's [installed to a server](/developers/quick-start/overview-of-apps#where-are-apps-installed).
On the left hand sidebar in your app's settings, there's a [**Bot** page](https://discord.com/developers/applications/select/bot) (where we fetched the token from). On this page, you can also configure settings like its [privileged intents](/developers/events/gateway#privileged-intents) or whether it can be installed by other users.
Intents determine which events Discord will send your app when you're creating a [Gateway API connection](/developers/events/gateway). For example, if you want your app to perform an action when users add a reaction to a message, you can pass the `GUILD_MESSAGE_REACTIONS` (`1 << 10`) intent.
Some intents are [privileged](/developers/events/gateway#privileged-intents), meaning they allow your app to access data that may be considered sensitive (like the contents of messages). Privileged intents can be toggled on the **Bot** page in your app's settings, but they must be approved before you [verify your app](https://support-dev.discord.com/hc/en-us/articles/23926564536471-How-Do-I-Get-My-App-Verified). Standard, non-privileged intents don't require any additional permissions or configurations.
More information about intents and a full list of available intents (along with their associated events) is in the [Gateway documentation](/developers/events/gateway#gateway-intents).
For now, we don't need to configure anything additional here, but you may need to in the future depending on your app's use case. Let's go ahead and get our app ready for installation.
### Choosing installation contexts
Now we'll select where your app can be installed in Discord, which is determined by the [installation contexts](/developers/resources/application#installation-context) that your app supports.
**Installation contexts** determine where your app can be installed: to servers, to users, or both. Apps can choose which installation contexts they support within the app's settings.
* Apps installed in a **[server context](/developers/resources/application#server-context)** (server-installed apps) must be authorized by a server member with the `MANAGE_GUILD` permission, and are visible to all members of the server.
* Apps installed in a **[user context](/developers/resources/application#user-context)** (user-installed apps) are visible only to the authorizing user, and therefore don't require any server-specific permissions. Apps installed to a user context are visible across all of the user's servers, DMs, and GDMs—however, they're limited to using commands.
Click on [**Installation**](https://discord.com/developers/applications/select/installation) in the left sidebar, then under **Installation Contexts** make sure both "User Install" and "Guild Install" are selected.
Some apps may only want to support one installation context—for example, a moderation app may only support a server context. However, by default, we recommend supporting both installation contexts. For detailed information about supporting user-installed apps, you can read the [user-installable app tutorial](/developers/tutorials/developing-a-user-installable-app).
### Setting up an install link
[Install links](/developers/resources/application#install-links) provide an easy way for users to install your app in Discord. We'll set up the default [Discord Provided Link](/developers/resources/application#discord-provided-link), but you can read more about the different type of install links in the [Application documentation](/developers/resources/application#types-of-install-links).
On the [**Installation** page](https://discord.com/developers/applications/select/installation), go to the **Install Link** section and select "Discord Provided Link" if it's not already selected.
When Discorded Provided Link is selected, a new **Default Install Settings** section will appear, which we'll configure next.
### Adding scopes and bot permissions
Apps need approval from installing users to perform actions in Discord (like creating a slash command or fetching a list of server members). Let's add scopes and permissions before installing the app.
When creating an app, scopes and permissions determine what your app can do and access in Discord.
* [OAuth2 Scopes](/developers/topics/oauth2#shared-resources-oauth2-scopes) determine what data access and actions your app can take, granted on behalf of an installing or authenticating user.
* [Permissions](/developers/topics/permissions#permission-overwrites) are the granular permissions for your bot user, the same as other users in Discord have. They can be approved by the installing user or later updated within server settings or with [permission overwrites](/developers/topics/permissions#permission-overwrites). Since apps installed to a user context can only respond to commands, these permissions are only relevant to apps installed to a server.
On the [**Installation** page](https://discord.com/developers/applications/select/installation) in the **Default Install Settings** section:
* For **User Install**, add the `applications.commands` scope
* For **Guild Install**, add the `applications.commands` scope and `bot` scope. When you select `bot`, a new **Permissions** menu will appear to select the bot user's permissions. Select any permissions that you may want for your app—for now, I'll just select `Send Messages`.
See a list of all [OAuth2 scopes](/developers/topics/oauth2#shared-resources-oauth2-scopes), or read more on [permissions](/developers/topics/permissions) in the documentation.
### Installing your app
When developing apps, you should build and test on your user account (for user-installable apps) and in a server that isn't actively used by others (for server-installable apps). If you don't have your own server already, you can [create one for free](https://support.discord.com/hc/en-us/articles/204849977-How-do-I-create-a-server-).
Once you add scopes, copy the URL from the **Install Link** section from before.
Since our app is supporting both installation contexts, we'll install your new app to both a test server and your user account so that we can test in both [installation contexts](/developers/resources/application#installation-context).
###### Install to server
To install your app to your test server, copy the default Install Link for your app from the **Installation** page. Paste the link in your browser and hit enter, then select "Add to server" in the installation prompt.
Select your test server, and follow the installation prompt. Once your app is added to your test server, you should see it appear in the member list.
###### Install to user account
Next, install your app to your user account. Paste the same Install Link in your browser and hit enter. This time, select "Add to my apps" in the installation prompt.
Follow the installation prompt to install your app to your user account. Once it's installed you can open a DM with it.
***
## Step 2: Running your app
With your app configured and installed to your test server and account, let's take a look at the code.
To make development a bit simpler, the app uses [discord-interactions](https://github.com/discord/discord-interactions-js), which provides types and helper functions. If you prefer to use other languages or libraries, check out the [Community Resources](/developers/developer-tools/community-resources) documentation.
### Installing slash commands
To install slash commands, the app is using [`node-fetch`](https://github.com/node-fetch/node-fetch). You can see the implementation for the installation in `utils.js` within the `DiscordRequest()` function.
The project contains a `register` script you can use to install the commands in `ALL_COMMANDS`, which is defined at the bottom of `commands.js`. It installs the commands as global commands by calling the HTTP API's [`PUT /applications//commands`](/developers/interactions/application-commands#bulk-overwrite-global-application-commands) endpoint.
If you want to customize your commands or add additional ones, you can reference the command structure in the [commands documentation](/developers/interactions/application-commands#application-command-object-application-command-structure).
In your terminal within the project folder, run the following command:
```
npm run register
```
If you navigate back to your server, you should see the slash commands appear. But if you try to run them, nothing will happen since your app isn't receiving or handling any requests from Discord.
Apps have access to [APIs](/developers/quick-start/overview-of-apps#what-apis-can-apps-use) that you can mix-and-match to build apps:
* **[HTTP API](/developers/reference#http-api)** is a REST-like API for general operations like sending and updating data in Discord, or fetching data about a resource.
* **[Gateway API](/developers/reference#gateway-websocket-api)** is a WebSocket-based API that is helpful for maintaining state or listening to events happening in a Discord server. We won't be using it in this guide, but more information about how to create a Gateway connection and the different events you can listen to are in the [Gateway documentation](/developers/events/gateway).
***
## Step 3: Handling interactivity
To enable your app to receive slash command and other interactions requests, Discord needs a public URL to send them. This URL can be configured in your app's settings as **Interaction Endpoint URL**.
### Set up a public endpoint
To set up a public endpoint, we'll start our app which runs an [Express](https://expressjs.com/) server, then use [ngrok](https://ngrok.com/) to expose our server publicly.
First, go to your project's folder and run the following to start your app:
```
npm run start
```
There should be output indicating your app is running on port `3000`. Behind the scenes, our app is ready to handle interactions from Discord, which includes verifying security request headers and responding to `PING` requests. We're skipping over a lot of the details in this tutorial, but details about preparing apps for interactions is in the [Interactions Overview](/developers/interactions/overview#preparing-for-interactions) documentation.
By default, the server will listen to requests sent to port 3000, but if you want to change the port, you can specify a `PORT` variable in your `.env` file.
Next, we'll start our ngrok tunnel. If you don't have ngrok installed locally, you can install it by following the instructions on the [ngrok download page](https://ngrok.com/download).
After ngrok is installed, open a new terminal and create a public endpoint that will forward requests to your Express server:
```
ngrok http 3000
```
You should see your connection open with output similar to the following:
```
Tunnel Status online
Version 2.0/2.0
Web Interface http://127.0.0.1:4040
Forwarding https://1234-someurl.ngrok.io -> localhost:3000
Connections ttl opn rt1 rt5 p50 p90
0 0 0.00 0.00 0.00 0.00
```
We'll use **Forwarding** URL as the publicly-accessible URL where Discord will send interactions requests in the next step.
### Adding an interaction endpoint URL
Go to your [app's settings](https://discord.com/developers/applications) and on the [**General Information** page](https://discord.com/developers/applications/select/information) under **Interaction Endpoint URL**, paste your new ngrok forwarding URL and append `/interactions`.
Click **Save Changes** and ensure your endpoint is successfully verified.
If you have troubles verifying your endpoint, make sure both ngrok and your app are running on the same port, and that you've copied the ngrok URL correctly
The verification is handled automatically by the sample app in two ways:
* It uses the `PUBLIC_KEY` and [discord-interactions package](https://github.com/discord/discord-interactions-js#usage) with a wrapper function (imported from `utils.js`) that makes it conform to [Express's `verify` interface](http://expressjs.com/en/5x/api.html#express.json). This is run on every incoming request to your app.
* It responds to incoming `PING` requests.
You can learn more about preparing your app to receive interactions in [the interactions documentation](/developers/interactions/overview#preparing-for-interactions).
### Handling slash command requests
With the endpoint verified, navigate to your project's `app.js` file and find the code block that handles the `/test` command:
```javascript theme={"system"}
// "test" command
if (name === 'test') {
// Send a message into the channel where command was triggered from
return res.send({
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
data: {
flags: InteractionResponseFlags.IS_COMPONENTS_V2,
components: [
{
type: MessageComponentTypes.TEXT_DISPLAY,
// Fetches a random emoji to send from a helper function
content: `hello world ${getRandomEmoji()}`
}
]
},
});
}
```
The code above is responding to the interaction with a message in the channel, DM, or Group DM it originated from. You can see all available response types, like responding with a modal, [in the documentation](/developers/interactions/receiving-and-responding#interaction-response-object-interaction-callback-type).
`InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE` is a constant [exported from `discord-interactions`](https://github.com/discord/discord-interactions-js/blob/main/src/index.ts#L33)
Go to your server and make sure your app's `/test` slash command works. When you trigger it, your app should send a message that contains “hello world” followed by a random emoji.
In the following section, we'll add an additional command that uses slash command options, buttons, and select menus to build the rock-paper-scissors game.
***
## Step 4: Adding message components
The `/challenge` command will be how our rock-paper-scissors-style game is initiated. When the command is triggered, the app will send message components to the channel, which will guide the users to complete the game.
### Adding a command with options
The `/challenge` command, called `CHALLENGE_COMMAND` in `commands.js`, has an array of `options`. In our app, the options are objects representing different things that a user can select while playing rock-paper-scissors, generated using keys of `RPSChoices` in `game.js`.
You can read more about command options and their structure [in the documentation](/developers/interactions/application-commands#application-command-object-application-command-option-structure).
While this guide won't touch much on the `game.js` file, feel free to poke around and change commands or the options in the commands.
To handle the `/challenge` command, add the following code after the `if name === “test”` if block:
```javascript theme={"system"}
// "challenge" command
if (name === 'challenge' && id) {
// Interaction context
const context = req.body.context;
// User ID is in user field for (G)DMs, and member for servers
const userId = context === 0 ? req.body.member.user.id : req.body.user.id;
// User's object choice
const objectName = req.body.data.options[0].value;
// Create active game using message ID as the game ID
activeGames[id] = {
id: userId,
objectName,
};
return res.send({
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
data: {
flags: InteractionResponseFlags.IS_COMPONENTS_V2,
components: [
{
type: MessageComponentTypes.TEXT_DISPLAY,
// Fetches a random emoji to send from a helper function
content: `Rock papers scissors challenge from <@${userId}>`,
},
{
type: MessageComponentTypes.ACTION_ROW,
components: [
{
type: MessageComponentTypes.BUTTON,
// Append the game ID to use later on
custom_id: `accept_button_${req.body.id}`,
label: 'Accept',
style: ButtonStyleTypes.PRIMARY,
},
],
},
],
},
});
}
```
If you aren't sure where to paste the code, you can see the full code in `examples/app.js`.
The above code is doing a few things:
1. Parses the request body to get the ID of the user who triggered the slash command (`userId`), and the option (object choice) they selected (`objectName`). If the interaction is run in a server (`context === 0`), the user ID will be nested in the `member` object. If it's in a DM or Group DM, it will be in the `user` object.
2. Adds a new game to the `activeGames` object using the interaction ID. The active game records the `userId` and `objectName`.
3. Sends a message back to the channel with a button with a `custom_id` of `accept_button_`.
The sample code uses an object as in-memory storage, but for production apps you should use a database.
When sending a message with [message components](/developers/components/reference), the individual payloads are appended to a `components` array. Actionable components (like buttons) need to be inside of an [action row](/developers/components/reference#action-row), which you can see in the code sample.
Note the unique `custom_id` sent with message components, in this case `accept_button_` with the active game's ID appended to it. A `custom_id` can be used to handle requests that Discord sends you when someone interacts with the component, which you'll see in a moment.
Now when you run the `/challenge` command and pick an option, your app will send a message with an **Accept** button. Let's add code to handle the button press.
When users interact with a message component, Discord will send a request with an [interaction type](/developers/interactions/receiving-and-responding#interaction-object-interaction-type) of `3` (or the `MESSAGE_COMPONENT` value when using `discord-interactions`).
To set up a handler for the button, we'll check the `type` of interaction, followed by matching the `custom_id`.
Paste the following code under the type handler for `APPLICATION_COMMAND`s:
```javascript theme={"system"}
if (type === InteractionType.MESSAGE_COMPONENT) {
// custom_id set in payload when sending message component
const componentId = data.custom_id;
if (componentId.startsWith('accept_button_')) {
// get the associated game ID
const gameId = componentId.replace('accept_button_', '');
// Delete message with token in request body
const endpoint = `webhooks/${process.env.APP_ID}/${req.body.token}/messages/${req.body.message.id}`;
try {
await res.send({
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
data: {
// Indicates it'll be an ephemeral message
flags: InteractionResponseFlags.EPHEMERAL | InteractionResponseFlags.IS_COMPONENTS_V2,
components: [
{
type: MessageComponentTypes.TEXT_DISPLAY,
content: 'What is your object of choice?',
},
{
type: MessageComponentTypes.ACTION_ROW,
components: [
{
type: MessageComponentTypes.STRING_SELECT,
// Append game ID
custom_id: `select_choice_${gameId}`,
options: getShuffledOptions(),
},
],
},
],
},
});
// Delete previous message
await DiscordRequest(endpoint, { method: 'DELETE' });
} catch (err) {
console.error('Error sending message:', err);
}
}
return;
}
```
The above code:
1. Checks for a `custom_id` that matches what we originally sent (in this case, it starts with `accept_button_`). The custom ID also has the active game ID appended, so we store that in `gameID`.
2. [Deletes the original message](/developers/interactions/receiving-and-responding#delete-original-interaction-response) calling a webhook using `node-fetch` and passing the unique interaction `token` in the request body. This is done to clean up the channel, and so other users can't click the button.
3. Responds to the request by sending a message that contains a select menu with the object choices for the game. The payload should look fairly similar to the previous one, with the exception of the `options` array and `flags: 64`, [which indicates that the message is ephemeral](/developers/interactions/receiving-and-responding#create-followup-message).
The `options` array is populated using the `getShuffledOptions()` method in `game.js`, which manipulates the `RPSChoices` values to conform to the shape of [string select options](/developers/components/reference#string-select-select-option-structure).
The last thing to add is code to handle select menu interactions, and to send the result of the game to channel.
Since select menus are just another message component, the code to handle their interactions will be almost identical to buttons.
Modify the code above to handle the select menu:
```javascript theme={"system"}
if (type === InteractionType.MESSAGE_COMPONENT) {
// custom_id set in payload when sending message component
const componentId = data.custom_id;
if (componentId.startsWith('accept_button_')) {
// get the associated game ID
const gameId = componentId.replace('accept_button_', '');
// Delete message with token in request body
const endpoint = `webhooks/${process.env.APP_ID}/${req.body.token}/messages/${req.body.message.id}`;
try {
await res.send({
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
data: {
// Indicates it'll be an ephemeral message
flags: InteractionResponseFlags.EPHEMERAL | InteractionResponseFlags.IS_COMPONENTS_V2,
components: [
{
type: MessageComponentTypes.TEXT_DISPLAY,
content: 'What is your object of choice?',
},
{
type: MessageComponentTypes.ACTION_ROW,
components: [
{
type: MessageComponentTypes.STRING_SELECT,
// Append game ID
custom_id: `select_choice_${gameId}`,
options: getShuffledOptions(),
},
],
},
],
},
});
// Delete previous message
await DiscordRequest(endpoint, { method: 'DELETE' });
} catch (err) {
console.error('Error sending message:', err);
}
} else if (componentId.startsWith('select_choice_')) {
// get the associated game ID
const gameId = componentId.replace('select_choice_', '');
if (activeGames[gameId]) {
// Interaction context
const context = req.body.context;
// Get user ID and object choice for responding user
// User ID is in user field for (G)DMs, and member for servers
const userId = context === 0 ? req.body.member.user.id : req.body.user.id;
const objectName = data.values[0];
// Calculate result from helper function
const resultStr = getResult(activeGames[gameId], {
id: userId,
objectName,
});
// Remove game from storage
delete activeGames[gameId];
// Update message with token in request body
const endpoint = `webhooks/${process.env.APP_ID}/${req.body.token}/messages/${req.body.message.id}`;
try {
// Send results
await res.send({
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
data: {
flags: InteractionResponseFlags.IS_COMPONENTS_V2,
components: [
{
type: MessageComponentTypes.TEXT_DISPLAY,
content: resultStr
}
]
},
});
// Update ephemeral message
await DiscordRequest(endpoint, {
method: 'PATCH',
body: {
components: [
{
type: MessageComponentTypes.TEXT_DISPLAY,
content: 'Nice choice ' + getRandomEmoji()
}
],
},
});
} catch (err) {
console.error('Error sending message:', err);
}
}
}
return;
}
```
Similar to earlier code, the code above is getting the user ID and their object selection from the interaction request.
That information, along with the original user's ID and selection from the `activeGames` object, are passed to the `getResult()` function. `getResult()` determines the winner, then builds a readable string to send back to the channel.
We're also calling another webhook, this time to [update the follow-up ephemeral message](/developers/interactions/receiving-and-responding#edit-followup-message) since it can't be deleted.
Finally, the results are sent in the channel using the `CHANNEL_MESSAGE_WITH_SOURCE` interaction response type.
....and that's it 🎊 Go ahead and test your app and make sure everything works.
***
## Next steps
Congrats on building your first Discord app! 🤖
Hopefully you learned a bit about Discord apps, how to configure them, and how to make them interactive. From here, you can continue building out your app or explore what else is possible.
}>
Explore the platform features and APIs you have access to when building an app on Discord
}>
Explore 1st party and community-built libraries and tools to speed up and simplify your development
}>
Tutorial on building and handling interactions for apps installed to a user
}>
Join our community to ask questions about the API, attend events hosted by the Discord platform team, and interact with other devs
# Overview of Discord Apps
Source: https://docs.discord.com/developers/quick-start/overview-of-apps
Build Discord apps to customize, extend, and enhance Discord for millions of users.
A **Discord application** is the core entity that represents your integration with the Discord platform. Every bot, Activity, and Social SDK integration is backed by an application registered in the [Discord Developer Portal](https://discord.com/developers/applications).
Applications hold your credentials, OAuth2 settings, bot configuration, and metadata. Whether you're building a simple command-response bot or a full game integration with voice chat and rich presence, it all starts with creating an application.
## The Three Types of Discord Apps
### Bots
Bots are automated user accounts operated by application code. They appear in servers with an `APP` tag and can respond to events, slash commands, and user actions.
Use bots when you want to:
* Add commands, moderation tools, or utilities to a server
* React to events (messages, joins, reactions) in real time
* Build interactive experiences with buttons, menus, and modals
* Post automated updates from external systems
Bots can be **guild-installed** (added to a specific server) or **user-installed** (available everywhere the user goes, without requiring server permissions).
### Activities
Activities are embedded web applications that run inside Discord channels. They're built with the [Embedded App SDK](/developers/developer-tools/embedded-app-sdk) and can be launched by any user in a channel or from a bot with the `LAUNCH_ACTIVITY` interaction callback.
Use Activities when you want to:
* Build a game or interactive experience that runs inside Discord
* Create collaborative tools (watch parties, drawing apps, trivia games)
* Leverage Discord's existing social context: voice, friends, servers
### Social Layer for Games
The [Discord Social SDK](/developers/discord-social-sdk/overview) lets you add Discord-powered social features to your game across PC, mobile, and console. You can integrate individual features alongside your existing social systems or build out a more complete Discord-powered experience.
Use the Social SDK when you want to:
* Add rich presence, friends lists, and voice chat to your game
* Support cross-platform social features with Discord account linking
* Let players interact through Discord without leaving your game
## Where Apps Install
| App Type | Install Target | What It Unlocks |
| --------------------- | -------------- | -------------------------------------------- |
| Bot (guild-installed) | A server | Access to server channels, members, events |
| Bot (user-installed) | A user | Available in DMs, group DMs, and any server |
| Activity | An application | Launchable from any voice channel |
| Social SDK | Your game | Social features embedded in your game client |
## What APIs Can Apps Use?
There are a handful of different APIs that you can pick and choose from based on your app's functionality and which Discord features you want to access. Below is a quick overview of the main APIs on the Discord developer platform, but you can read more details and information about API usage in the [API reference](/developers/reference).
### HTTP API
The **HTTP API** is a REST API that lets you interact and modify core Discord resources like [channels](/developers/resources/channel), [servers (or guilds)](/developers/resources/guild), [users](/developers/resources/user), and [messages](/developers/resources/message#message-object).
Use the HTTP API to:
* Retrieve information about a resource
* Create, update, or delete a resource
Read details about using the HTTP API in the [API reference](/developers/reference#http-api).
### Gateway API
The **Gateway API** is a WebSocket connection between your app and Discord. While it's most commonly used to receive a real-time stream of events from Discord, the connection is bidirectional. Your app can also send events to Discord over the same connection.
Use the Gateway API to:
* Receive real-time events from Discord (messages, member joins, reactions, voice state changes, and more)
* Update your bot's presence and status
* Manage voice state and connect to voice channels
* Perform the connection handshake and manage the lifecycle of your Gateway connection
Read details about using the Gateway API in the [API reference](/developers/reference#gateway-websocket-api).
## Start Building
}>
Build a bot that responds to events, commands, and user interactions in Discord servers.
}>
Add Discord-powered social features to your game using the Discord Social SDK.
}>
Build an embedded web app or game that runs inside Discord voice channels.
# API Reference
Source: https://docs.discord.com/developers/reference
The Discord API is a REST API that allows you to interact with Discord data from your own applications. It's the primary way to interact with Discord from your own code.
**Base URL**
```bash theme={"system"}
https://discord.com/api
```
## API Versioning
Some API and Gateway versions are now non-functioning, and are labeled as discontinued in the table below for posterity. Trying to use these versions will fail and return 400 Bad Request.
Discord exposes different versions of our API[.](https://c.tenor.com/BuZl66EegkgAAAAC/westworld-dolores.gif) You should specify which version to use by including it in the request path like `https://discord.com/api/v{version_number}`. Omitting the version number from the route will route requests to the current default version (marked below). You can find the change log for the newest API version [here](/developers/change-log).
**API Versions**
| Version | Status | Default |
| ------- | ------------ | --------------------- |
| 10 | Available | |
| 9 | Available | |
| 8 | Deprecated | |
| 7 | Deprecated | |
| 6 | Deprecated | |
| 5 | Discontinued | |
| 4 | Discontinued | |
| 3 | Discontinued | |
## Error Messages
Starting in API v8, we've improved error formatting in form error responses. The response will tell you which JSON key contains the error, the error code, and a human readable error message. We will be frequently adding new error messages, so a complete list of errors is not feasible and would be almost instantly out of date. Here are some examples instead:
**Array Error**
```json theme={"system"}
{
"code": 50035,
"errors": {
"activities": {
"0": {
"platform": {
"_errors": [
{
"code": "BASE_TYPE_CHOICES",
"message": "Value must be one of ('desktop', 'android', 'ios')."
}
]
},
"type": {
"_errors": [
{
"code": "BASE_TYPE_CHOICES",
"message": "Value must be one of (0, 1, 2, 3, 4, 5)."
}
]
}
}
}
},
"message": "Invalid Form Body"
}
```
**Object Error**
```json theme={"system"}
{
"code": 50035,
"errors": {
"access_token": {
"_errors": [
{
"code": "BASE_TYPE_REQUIRED",
"message": "This field is required"
}
]
}
},
"message": "Invalid Form Body"
}
```
**Request Error**
```json theme={"system"}
{
"code": 50035,
"message": "Invalid Form Body",
"errors": {
"_errors": [
{
"code": "APPLICATION_COMMAND_TOO_LARGE",
"message": "Command exceeds maximum size (8000)"
}
]
}
}
```
## Authentication
Authenticating with the Discord API can be done in one of two ways:
1. Using a bot token found on the Bot page within your app's settings. For more information on bots see [bots vs user accounts](/developers/topics/oauth2#bot-vs-user-accounts).
2. Using an OAuth2 bearer token gained through the [OAuth2 API](/developers/topics/oauth2).
For all authentication types, authentication is performed with the `Authorization` HTTP header in the format `Authorization: TOKEN_TYPE TOKEN`.
**Example Bot Token Authorization Header**
```bash theme={"system"}
Authorization: Bot MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs
```
**Example Bearer Token Authorization Header**
```bash theme={"system"}
Authorization: Bearer CZhtkLDpNYXgPH9Ml6shqh2OwykChw
```
## Encryption
All HTTP-layer services and protocols (e.g. HTTP, WebSocket) within the Discord API are using TLS 1.2.
## Snowflakes
Discord utilizes Twitter's [snowflake](https://github.com/twitter-archive/snowflake/tree/snowflake-2010) format for uniquely identifiable descriptors (IDs). These IDs are guaranteed to be unique across all of Discord, except in some unique scenarios in which child objects share their parent's ID. Because Snowflake IDs are up to 64 bits in size (e.g. a uint64), they are always returned as strings in the HTTP API to prevent integer overflows in some languages. See [Gateway ETF/JSON](/developers/events/gateway#encoding-and-compression) for more information regarding Gateway encoding.
**Snowflake ID Broken Down in Binary**
```
111111111111111111111111111111111111111111 11111 11111 111111111111
64 22 17 12 0
```
**Snowflake ID Format Structure (Left to Right)**
| Field | Bits | Number of bits | Description | Retrieval |
| ------------------- | -------- | -------------- | ---------------------------------------------------------------------------- | ----------------------------------- |
| Timestamp | 63 to 22 | 42 bits | Milliseconds since Discord Epoch, the first second of 2015 or 1420070400000. | `(snowflake >> 22) + 1420070400000` |
| Internal worker ID | 21 to 17 | 5 bits | | `(snowflake & 0x3E0000) >> 17` |
| Internal process ID | 16 to 12 | 5 bits | | `(snowflake & 0x1F000) >> 12` |
| Increment | 11 to 0 | 12 bits | For every ID that is generated on that process, this number is incremented | `snowflake & 0xFFF` |
### Convert Snowflake to DateTime
### Snowflake IDs in Pagination
We typically use snowflake IDs in many of our API routes for pagination. The standardized pagination paradigm we utilize is one in which you can specify IDs `before` and `after` in combination with `limit` to retrieve a desired page of results. You will want to refer to the specific endpoint documentation for details.
It is useful to note that snowflake IDs are just numbers with a timestamp, so when dealing with pagination where you want results from the beginning of time (in Discord Epoch, but `0` works here too) or before/after a specific time you can generate a snowflake ID for that time.
**Generating a snowflake ID from a Timestamp Example**
```bash theme={"system"}
(timestamp_ms - DISCORD_EPOCH) << 22
```
## ID Serialization
There are some cases in which our API and Gateway may return IDs in an unexpected format. Internally, Discord stores IDs as integer snowflakes. When we serialize IDs to JSON, we transform `bigints` into strings. Given that all Discord IDs are snowflakes, you should always expect a string.
However, there are cases in which passing something to our API will instead return IDs serialized as an integer; this is the case when you send our API or Gateway a value in an `id` field that is not `bigint` size. For example, when requesting `GUILD_MEMBERS_CHUNK` from our gateway:
```bash theme={"system"}
// Send
{
op: 8,
d: {
guild_id: '308994132968210433',
user_ids: [ '123123' ]
}
}
// Receive
{
t: 'GUILD_MEMBERS_CHUNK',
s: 3,
op: 0,
d: {
not_found: [ 123123 ],
members: [],
guild_id: '308994132968210433'
}
}
```
You can see in this case that the sent `user_id` is not a `bigint`; therefore, when it is serialized back to JSON by Discord, it is not transformed into a string. This will never happen with IDs that come from Discord. But, this can happen if you send malformed data in your requests.
## ISO8601 Date/Time
Discord utilizes the [ISO8601 format](https://www.loc.gov/standards/datetime/iso-tc154-wg5_n0038_iso_wd_8601-1_2016-02-16.pdf) for most Date/Times returned in our models. This format is referred to as type `ISO8601` within tables in this documentation.
## Nullable and Optional Resource Fields
Resource fields that may contain a `null` value have types that are prefixed with a question mark. Resource fields that are optional have names that are suffixed with a question mark.
**Example Nullable and Optional Fields**
| Field | Type |
| ------------------------------- | ------- |
| optional\_field? | string |
| nullable\_field | ?string |
| optional\_and\_nullable\_field? | ?string |
## Consistency
Discord operates at a scale where true consistency is impossible. Because of this, lots of operations in our API and in-between our services are [eventually consistent](https://en.wikipedia.org/wiki/Eventual_consistency). Due to this, client actions can never be serialized and may be executed in any order (if executed at all). Along with these constraints, events in Discord may:
* Never be sent to a client
* Be sent exactly one time to the client
* Be sent up to N times per client
Clients should operate on events and results from the API in as much of an idempotent behavior as possible.
## HTTP API
### User Agent
Clients using the HTTP API must provide a valid [User Agent](https://www.rfc-editor.org/rfc/rfc9110.html#section-10.1.5) which specifies information about the client library and version in the following format:
**User Agent Example**
```bash theme={"system"}
User-Agent: DiscordBot ($url, $versionNumber)
```
Clients may append more information and metadata to the end of this string as they wish.
Client requests that do not have a valid User Agent specified may be blocked and return a [Cloudflare error](https://support.cloudflare.com/hc/en-us/articles/360029779472-Troubleshooting-Cloudflare-1XXX-errors).
### Content Type
Clients using the HTTP API must provide a valid `Content-Type` header, either `application/json`, `application/x-www-form-urlencoded`, or `multipart/form-data`, except where specified. Failing to do so will result in a `50035` "Invalid form body" error.
### Rate Limiting
The HTTP API implements a process for limiting and preventing excessive requests in accordance with [RFC 6585](https://tools.ietf.org/html/rfc6585#section-4). API users that regularly hit and ignore rate limits will have their API keys revoked, and be blocked from the platform. For more information on rate limiting of requests, please see the [Rate Limits](/developers/topics/rate-limits) section.
### Boolean Query Strings
Certain endpoints in the API are documented to accept booleans for their query string parameters. While there is no standard system for boolean representation in query string parameters, Discord represents such cases using `True`, `true`, or `1` for true and `False`, `false` or `0` for false.
### Array Query Strings
Certain endpoints in the API are documented to accept arrays for their query string parameters. Unless otherwise specified, Discord represents such cases using multiple instances of the same query string parameter. For example, `?id=123&id=456` for `["123", "456"]`.
## Gateway (WebSocket) API
Discord's Gateway API is used for maintaining persistent, stateful websocket connections between your client and our servers. These connections are used for sending and receiving real-time events your client can use to track and update local state. The Gateway API uses secure websocket connections as specified in [RFC 6455](https://tools.ietf.org/html/rfc6455). For information on opening Gateway connections, please see the [Gateway API](/developers/events/gateway#connections) section.
## Message Formatting
Discord utilizes a subset of markdown for rendering message content on its clients, while also adding some custom functionality to enable things like mentioning users and channels. This functionality uses the following formats:
**Formats**
| Type | Structure | Example |
| ----------------------------------- | ---------------------------------------- | ------------------------------------- |
| User | `<@USER_ID>` | `<@80351110224678912>` |
| User \* | `<@!USER_ID>` | `<@!80351110224678912>` |
| Channel | `<#CHANNEL_ID>` | `<#103735883630395392>` |
| Role | `<@&ROLE_ID>` | `<@&165511591545143296>` |
| Slash command | `` | `` |
| Slash command with subcommand | `` | `` |
| Slash command with subcommand group | `` | `` |
| Standard emoji | Unicode characters | 🪴 |
| Custom emoji | `<:NAME:ID>` | `<:mmLol:216154654256398347>` |
| Animated custom emoji | `` | `` |
| Unix timestamp | `` | `` |
| Styled unix timestamp | `` | `` |
| Guild navigation | `` | See below |
Using the markdown for users or roles will mention the target(s), and notify them depending on the sender's permissions as well as the value of the `allowed_mentions` field when creating a message.
Standard emoji are currently rendered using [Twemoji](https://github.com/jdecked/twemoji) for Desktop and Android while iOS devices use Apple's native emoji set.
Timestamps are expressed in **seconds** and display the given timestamp in the user's timezone and locale.
\* User mentions with an exclamation point are deprecated and should be handled like any other user mention.
**Timestamp Styles**
| Style | Example Output | Description |
| ----- | -------------------------------- | ----------------------- |
| t | 16:20 | Short Time |
| T | 16:20:30 | Medium Time |
| d | 20/04/2021 | Short Date |
| D | April 20, 2021 | Long Date |
| f \* | April 20, 2021 at 16:20 | Long Date, Short Time |
| F | Tuesday, April 20, 2021 at 16:20 | Full Date, Short Time |
| s | 20/04/2021, 16:20 | Short Date, Short Time |
| S | 20/04/2021, 16:20:30 | Short Date, Medium Time |
| R | 4 years ago | Relative Time |
\*default
**Guild Navigation Types**
Guild navigation types link to the corresponding resource in the current server.
| Full Syntax | Linked Resource |
| ---------------------- | -------------------------------------------------------------------------------------------------- |
| `` | Channel & Roles tab with [Onboarding prompts](/developers/resources/guild#guild-onboarding-object) |
| `` | Browse Channels tab |
| `` | [Server Guide](https://support.discord.com/hc/en-us/articles/13497665141655) tab |
| `` | [Linked Roles](https://support.discord.com/hc/en-us/articles/10388356626711) tab |
| `` | Specific linked role, opening the connection modal on click (the second `id` is the role id) |
## Image Formatting
**Image Base URL**
```bash theme={"system"}
https://cdn.discordapp.com/
```
Discord uses ids and hashes to render images in the client. These hashes can be retrieved through various API requests, like [Get User](/developers/resources/user#get-user). Below are the formats, size limitations, and CDN endpoints for images in Discord. The returned format can be changed by changing the [extension name](/developers/reference#image-formatting-image-formats) at the end of the URL. The returned size can be changed by appending a querystring of `?size=desired_size` to the URL. Image size can be any power of two between 16 and 4096.
Animated images uploaded as WebP or AVIF do not convert cleanly to GIF. Apps should request animated images as WebP for maximum compatibility, as this works regardless of the original upload format.
**Image Formats**
| Name | Extension |
| ------ | ----------- |
| JPEG | .jpg, .jpeg |
| PNG | .png |
| WebP | .webp |
| GIF | .gif |
| Lottie | .json |
**CDN Endpoints**
| Type | Path | Supports |
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- |
| Custom Emoji | emojis/[emoji\_id](/developers/resources/emoji#emoji-object).png \*\*\*\*\* | PNG, JPEG, WebP, GIF |
| Guild Icon | icons/[guild\_id](/developers/resources/guild#guild-object)/[guild\_icon](/developers/resources/guild#guild-object).png \* | PNG, JPEG, WebP, GIF |
| Guild Splash | splashes/[guild\_id](/developers/resources/guild#guild-object)/[guild\_splash](/developers/resources/guild#guild-object).png | PNG, JPEG, WebP |
| Guild Discovery Splash | discovery-splashes/[guild\_id](/developers/resources/guild#guild-object)/[guild\_discovery\_splash](/developers/resources/guild#guild-object).png | PNG, JPEG, WebP |
| Guild Banner | banners/[guild\_id](/developers/resources/guild#guild-object)/[guild\_banner](/developers/resources/guild#guild-object).png \* | PNG, JPEG, WebP, GIF |
| User Banner | banners/[user\_id](/developers/resources/user#user-object)/[user\_banner](/developers/resources/user#user-object).png \* | PNG, JPEG, WebP, GIF |
| Default User Avatar | embed/avatars/[index](/developers/resources/user#user-object).png \*\* \*\*\* | PNG |
| User Avatar | avatars/[user\_id](/developers/resources/user#user-object)/[user\_avatar](/developers/resources/user#user-object).png \* | PNG, JPEG, WebP, GIF |
| Guild Member Avatar | guilds/[guild\_id](/developers/resources/guild#guild-object)/users/[user\_id](/developers/resources/user#user-object)/avatars/[member\_avatar](/developers/resources/guild#guild-member-object).png \* | PNG, JPEG, WebP, GIF |
| Avatar Decoration | avatar-decoration-presets/[avatar\_decoration\_data\_asset](/developers/resources/user#avatar-decoration-data-object).png | PNG |
| Application Icon | app-icons/[application\_id](/developers/resources/application#application-object)/[icon](/developers/resources/application#application-object).png | PNG, JPEG, WebP |
| Application Cover | app-icons/[application\_id](/developers/resources/application#application-object)/[cover\_image](/developers/resources/application#application-object).png | PNG, JPEG, WebP |
| Application Asset | app-assets/[application\_id](/developers/resources/application#application-object)/[asset\_id](/developers/events/gateway-events#activity-object-activity-assets).png | PNG, JPEG, WebP |
| Achievement Icon | app-assets/[application\_id](/developers/resources/application#application-object)/achievements/[achievement\_id](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/docs/game_sdk/Achievements.md#user-achievement-struct)/icons/[icon\_hash](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/docs/game_sdk/Achievements.md#user-achievement-struct).png | PNG, JPEG, WebP |
| Store Page Asset | app-assets/[application\_id](/developers/resources/application#application-object)/store/asset\_id | PNG, JPEG, WebP |
| Sticker Pack Banner | app-assets/710982414301790216/store/[sticker\_pack\_banner\_asset\_id](/developers/resources/sticker#sticker-pack-object).png | PNG, JPEG, WebP |
| Team Icon | team-icons/[team\_id](/developers/topics/teams#data-models-team-object)/[team\_icon](/developers/topics/teams#data-models-team-object).png | PNG, JPEG, WebP |
| Sticker | stickers/[sticker\_id](/developers/resources/sticker#sticker-object).png \*\*\* \*\*\*\* | PNG, Lottie, GIF |
| Role Icon | role-icons/[role\_id](/developers/topics/permissions#role-object)/[role\_icon](/developers/topics/permissions#role-object).png | PNG, JPEG, WebP |
| Guild Scheduled Event Cover | guild-events/[scheduled\_event\_id](/developers/resources/guild-scheduled-event#guild-scheduled-event-object)/[scheduled\_event\_cover\_image](/developers/resources/guild-scheduled-event#guild-scheduled-event-object).png | PNG, JPEG, WebP |
| Guild Member Banner | guilds/[guild\_id](/developers/resources/guild#guild-object)/users/[user\_id](/developers/resources/user#user-object)/banners/[member\_banner](/developers/resources/guild#guild-member-object).png \* | PNG, JPEG, WebP, GIF |
| Guild Tag Badge | guild-tag-badges/[guild\_id](/developers/resources/guild#guild-object)/[badge\_hash](/developers/resources/user#user-object-user-primary-guild).png | PNG, JPEG, WebP |
\* In the case of endpoints that support GIFs, the hash will begin with `a_` if it is available in an animated format (example: `a_1269e74af4df7417b13759eae50c83dc`). These images can be retrieved as animated WebP using the `.webp` file extension and `?animated=true` querystring parameter.
\*\* In the case of the Default User Avatar endpoint, the value for `index` depends on whether the user is [migrated to the new username system](/developers/change-log#unique-usernames-on-discord). For users on the new username system, `index` will be `(user_id >> 22) % 6`. For users on the legacy username system, `index` will be `discriminator % 5`.
\*\*\* In the case of the Default User Avatar and Sticker endpoints, the size of images returned is constant with the "size" querystring parameter being ignored.
\*\*\*\* In the case of the Sticker endpoint, the sticker will be available as PNG if its [`format_type`](/developers/resources/sticker#sticker-object) is `PNG` or `APNG`, GIF if its `format_type` is `GIF`, and as [Lottie](https://airbnb.io/lottie/#/) if its `format_type` is `LOTTIE`.
\*\*\*\*\* For Custom Emoji, we highly recommend requesting emojis as WebP for maximum performance and compatibility. Emojis can be uploaded as JPEG, PNG, GIF, WebP, and AVIF formats. WebP and AVIF formats must be requested as WebP since they don't convert well to other formats. The Discord client uses WebP for all emojis displayed in-app. See the [Emoji Resource](/developers/resources/emoji) page for more details.
Sticker GIFs do not use the CDN base URL, and can be accessed at `https://media.discordapp.net/stickers/.gif`.
## Image Data
Image data is a [Data URI scheme](https://en.wikipedia.org/wiki/Data_URI_scheme) that supports JPG, GIF, and PNG formats. An example Data URI format is:
```
data:image/jpeg;base64,BASE64_ENCODED_JPEG_IMAGE_DATA
```
Ensure you use the proper content type (`image/jpeg`, `image/png`, `image/gif`) that matches the image data being provided.
### **Signed Attachment CDN URLs**
Attachments uploaded to Discord's CDN (like user and bot-uploaded images) have signed URLs with a preset expiry time. Discord automatically refreshes attachment CDN URLs that appear within the client, so when your app receives a payload with a signed URL (like when you [fetch a message](/developers/resources/message#get-channel-message)), it will be valid.
When passing CDN URLs into API fields, like [`url` in an embed image object](/developers/resources/message#embed-object-embed-image-structure) and [`avatar_url` for webhooks](/developers/resources/webhook#execute-webhook-jsonform-params), your app can pass the CDN URL without any parameters as the value and Discord will automatically render and refresh the URL.
The [standard CDN endpoints](/developers/reference#image-formatting-cdn-endpoints) listed above are not signed, so they will not expire.
###### Example Attachment CDN URL
```
https://cdn.discordapp.com/attachments/1012345678900020080/1234567891233211234/my_image.png?ex=65d903de&is=65c68ede&hm=2481f30dd67f503f54d020ae3b5533b9987fae4e55f2b4e3926e08a3fa3ee24f&
```
###### Attachment CDN URL Parameters
| Parameter | Description |
| --------- | --------------------------------------------------------------- |
| ex | Hex timestamp indicating when an attachment CDN URL will expire |
| is | Hex timestamp indicating when the URL was issued |
| hm | Unique signature that remains valid until the URL's expiration |
## Uploading Files
The file upload size limit applies to each file in a request. The default limit is `10 MiB` for all users, but may be higher for users depending on their [Nitro](https://support.discord.com/hc/en-us/articles/115000435108-What-are-Nitro-Nitro-Basic) status or by the server's [Boost Tier](https://support.discord.com/hc/en-us/articles/360028038352-Server-Boosting-FAQ-#h_419c3bd5-addd-4989-b7cf-c7957ef92583). The `attachment_size_limit` value provided [when working with interactions](/developers/interactions/receiving-and-responding#interaction-object-interaction-structure) is calculated as the maximum of these values.
Some endpoints support file attachments, indicated by the `files[n]` parameter. To add file(s), the standard `application/json` body must be replaced by a `multipart/form-data` body. The JSON message body can optionally be provided using the `payload_json` parameter.
All `files[n]` parameters must include a valid `Content-Disposition` subpart header with a `filename` and unique `name` parameter. Each file parameter must be uniquely named in the format `files[n]` such as `files[0]`, `files[1]`, or `files[42]`. The suffixed index `n` is the snowflake placeholder that can be used in the `attachments` field, which can be passed to the `payload_json` parameter (or [Callback Data Payloads](/developers/interactions/receiving-and-responding#interaction-response-object-interaction-callback-data-structure)).
Images can also be referenced in embeds using the `attachment://filename` URL. An example payload is provided below.
### Editing Message Attachments
The `attachments` JSON parameter includes all files that will be appended to the message, including new files and their respective snowflake placeholders (referenced above). When making a `PATCH` request, only files listed in the `attachments` parameter will be appended to the message. Any previously-added files that aren't included will be removed.
The `description` and `is_spoiler` fields of existing attachments can optionally be updated.
**Example Request Bodies (multipart/form-data)**
Note that these examples are small sections of an HTTP request to demonstrate behavior of this endpoint - client libraries will set their own form boundaries (`boundary` is just an example). For more information, refer to the [multipart/form-data spec](https://tools.ietf.org/html/rfc7578#section-4).
This example demonstrates usage of the endpoint without `payload_json`.
```bash theme={"system"}
--boundary
Content-Disposition: form-data; name="content"
Hello, World!
--boundary
Content-Disposition: form-data; name="tts"
true
--boundary--
```
This example demonstrates usage of the endpoint with `payload_json` and all content fields (`content`, `embeds`, `files[n]`) set.
```bash theme={"system"}
--boundary
Content-Disposition: form-data; name="payload_json"
Content-Type: application/json
{
"content": "Hello, World!",
"embeds": [{
"title": "Hello, Embed!",
"description": "This is an embedded message.",
"thumbnail": {
"url": "attachment://myfilename.png"
},
"image": {
"url": "attachment://mygif.gif"
}
}],
"message_reference": {
"message_id": "233648473390448641"
},
"attachments": [{
"id": 0,
"description": "Image of a cute little cat",
"filename": "myfilename.png"
}, {
"id": 1,
"description": "Rickroll gif",
"filename": "mygif.gif"
}]
}
--boundary
Content-Disposition: form-data; name="files[0]"; filename="myfilename.png"
Content-Type: image/png
[image bytes]
--boundary
Content-Disposition: form-data; name="files[1]"; filename="mygif.gif"
Content-Type: image/gif
[image bytes]
--boundary--
```
**Using Attachments within Embeds**
You can upload attachments when creating a message and use those attachments within your embed. To do this, you will want to upload files as part of your `multipart/form-data` body. Make sure that you're uploading files which contain a filename, as you will need to reference it in your payload.
Only `.jpg`, `.jpeg`, `.png`, `.webp`, and `.gif` may be used at this time. Other file types are not supported.
Within an embed object, you can set an image to use an attachment as its URL with the attachment scheme syntax: `attachment://filename.png`
For example:
```bash theme={"system"}
{
"embeds": [{
"image": {
"url": "attachment://screenshot.png"
}
}]
}
```
## Locales
| Locale | Language Name | Native Name |
| ------ | --------------------- | ------------------- |
| id | Indonesian | Bahasa Indonesia |
| da | Danish | Dansk |
| de | German | Deutsch |
| en-GB | English, UK | English, UK |
| en-US | English, US | English, US |
| es-ES | Spanish | Español |
| es-419 | Spanish, LATAM | Español, LATAM |
| fr | French | Français |
| hr | Croatian | Hrvatski |
| it | Italian | Italiano |
| lt | Lithuanian | Lietuviškai |
| hu | Hungarian | Magyar |
| nl | Dutch | Nederlands |
| no | Norwegian | Norsk |
| pl | Polish | Polski |
| pt-BR | Portuguese, Brazilian | Português do Brasil |
| ro | Romanian, Romania | Română |
| fi | Finnish | Suomi |
| sv-SE | Swedish | Svenska |
| vi | Vietnamese | Tiếng Việt |
| tr | Turkish | Türkçe |
| cs | Czech | Čeština |
| el | Greek | Ελληνικά |
| bg | Bulgarian | български |
| ru | Russian | Pусский |
| uk | Ukrainian | Українська |
| hi | Hindi | हिन्दी |
| th | Thai | ไทย |
| zh-CN | Chinese, China | 中文 |
| ja | Japanese | 日本語 |
| zh-TW | Chinese, Taiwan | 繁體中文 |
| ko | Korean | 한국어 |
# Application Resource
Source: https://docs.discord.com/developers/resources/application
Reference for Discord application objects and management endpoints.
[Applications](/developers/quick-start/overview-of-apps) (or "apps") are containers for developer platform features, and can be installed to Discord servers and/or user accounts.
### Application Object
###### Application Structure
| Field | Type | Description |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| id | snowflake | ID of the app |
| name | string | Name of the app |
| icon | ?string | [Icon hash](/developers/reference#image-formatting) of the app |
| description | string | Description of the app |
| rpc\_origins? | array of strings | List of RPC origin URLs, if RPC is enabled |
| bot\_public | boolean | When `false`, only the app owner can add the app to guilds |
| bot\_require\_code\_grant | boolean | When `true`, the app's bot will only join upon completion of the full OAuth2 code grant flow |
| bot? | partial [user](/developers/resources/user#user-object) object | Partial user object for the bot user associated with the app |
| terms\_of\_service\_url? | string | URL of the app's Terms of Service |
| privacy\_policy\_url? | string | URL of the app's Privacy Policy |
| owner? | partial [user](/developers/resources/user#user-object) object | Partial user object for the owner of the app |
| verify\_key | string | Hex encoded key for verification in interactions and the GameSDK's [GetTicket](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/docs/game_sdk/Applications.md#getticket) |
| team | ?[team](/developers/topics/teams#data-models-team-object) object | If the app belongs to a team, this will be a list of the members of that team |
| guild\_id? | snowflake | Guild associated with the app. For example, a developer support server. |
| guild? | partial [guild](/developers/resources/guild#guild-object) object | Partial object of the associated guild |
| primary\_sku\_id? | snowflake | If this app is a game sold on Discord, this field will be the id of the "Game SKU" that is created, if exists |
| slug? | string | If this app is a game sold on Discord, this field will be the URL slug that links to the store page |
| cover\_image? | string | App's default rich presence invite [cover image hash](/developers/reference#image-formatting) |
| flags? | integer | App's public [flags](/developers/resources/application#application-object-application-flags) |
| approximate\_guild\_count? | integer | Approximate count of guilds the app has been added to |
| approximate\_user\_install\_count? | integer | Approximate count of users that have installed the app (authorized with `application.commands` as a scope) |
| approximate\_user\_authorization\_count? | integer | Approximate count of users that have OAuth2 authorizations for the app |
| redirect\_uris? | array of strings | Array of redirect URIs for the app |
| interactions\_endpoint\_url? | ?string | [Interactions endpoint URL](/developers/interactions/receiving-and-responding#receiving-an-interaction) for the app |
| role\_connections\_verification\_url? | ?string | Role connection verification URL for the app |
| event\_webhooks\_url? | ?string | [Event webhooks URL](/developers/events/webhook-events#preparing-for-events) for the app to receive webhook events |
| event\_webhooks\_status? | [application event webhook status](/developers/resources/application#application-object-application-event-webhook-status) | If [webhook events](/developers/events/webhook-events) are enabled for the app. `1` (default) means disabled, `2` means enabled, and `3` means disabled by Discord |
| event\_webhooks\_types? | array of strings | List of [Webhook event types](/developers/events/webhook-events#event-types) the app subscribes to |
| tags? | array of strings | List of tags describing the content and functionality of the app. Max of 5 tags. |
| install\_params? | [install params](/developers/resources/application#install-params-object) object | Settings for the app's default in-app authorization link, if enabled |
| integration\_types\_config? | dictionary with keys of [application integration types](/developers/resources/application#application-object-application-integration-types) | Default scopes and permissions for each supported installation context. Value for each key is an [integration type configuration object](/developers/resources/application#application-object-application-integration-type-configuration-object) |
| custom\_install\_url? | string | Default custom authorization URL for the app, if enabled |
###### Example Application Object
```json theme={"system"}
{
"bot_public": true,
"bot_require_code_grant": false,
"cover_image": "31deabb7e45b6c8ecfef77d2f99c81a5",
"description": "Test",
"flags": 8192,
"flags_new": "8192",
"guild_id": "290926798626357260",
"icon": null,
"id": "172150183260323840",
"integration_types_config": {
"0": {
"oauth2_install_params": {
"scopes": [
"applications.commands",
"bot"
],
"permissions": "2048"
}
},
"1": {
"oauth2_install_params": {
"scopes": [
"applications.commands"
],
"permissions": "0"
}
}
},
"name": "Baba O-Riley",
"interactions_endpoint_url": null,
"role_connections_verification_url": null,
"event_webhooks_url": null,
"event_webhooks_status": 1,
"owner": {
"avatar": null,
"discriminator": "1738",
"flags": 1024,
"id": "172150183260323840",
"username": "i own a bot"
},
"primary_sku_id": "172150183260323840",
"slug": "test",
"team": {
"name": "A team",
"icon": "dd9b7dcfdf5351b9c3de0fe167bacbe1",
"id": "531992624043786253",
"owner_user_id": "511972282709709995",
"members": [
{
"membership_state": 2,
"permissions": ["*"],
"team_id": "531992624043786253",
"user": {
"avatar": "d9e261cd35999608eb7e3de1fae3688b",
"discriminator": "0001",
"id": "511972282709709995",
"username": "Mr Owner"
}
}
]
},
"verify_key": "1e0a356058d627ca38a5c8c9648818061d49e49bd9da9e3ab17d98ad4d6bg2u8"
}
```
###### Application Integration Types
Where an app can be installed, also called its supported [installation contexts](/developers/resources/application#installation-context).
| Type | ID | Description |
| --------------- | -- | ----------------------------- |
| `GUILD_INSTALL` | 0 | App is installable to servers |
| `USER_INSTALL` | 1 | App is installable to users |
###### Application Integration Type Configuration Object
| Field | Type | Description |
| ------------------------ | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| oauth2\_install\_params? | [install params object](/developers/resources/application#install-params-object) | Install params for each installation context's default in-app authorization link |
###### Application Event Webhook Status
Status indicating whether event webhooks are enabled or disabled for an application.
| Name | Value | Description |
| --------------------- | ----- | ----------------------------------------------------------------- |
| `DISABLED` | `1` | Webhook events are disabled by developer |
| `ENABLED` | `2` | Webhook events are enabled by developer |
| `DISABLED_BY_DISCORD` | `3` | Webhook events are disabled by Discord, usually due to inactivity |
###### Application Flags
The `flags` field is serialized as a number; however, this number will not grow beyond 31 bits. New flag bits beyond bit 30 will only appear in `flags_new`, a string-serialized integer containing the full set of flag bits. Existing integrations consuming `flags` are not impacted. `flags_new` is for response serialization only — requests that accept flag values should continue to use the original `flags` field.
| Value | Name | Description |
| --------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `1 << 6` | APPLICATION\_AUTO\_MODERATION\_RULE\_CREATE\_BADGE | Indicates if an app uses the [Auto Moderation API](/developers/resources/auto-moderation) |
| `1 << 12` | GATEWAY\_PRESENCE | Intent required for bots in **100 or more servers** to receive [`presence_update` events](/developers/events/gateway-events#presence-update) |
| `1 << 13` | GATEWAY\_PRESENCE\_LIMITED | Intent required for bots in under 100 servers to receive [`presence_update` events](/developers/events/gateway-events#presence-update), found on the **Bot** page in your app's settings |
| `1 << 14` | GATEWAY\_GUILD\_MEMBERS | Intent required for bots in **100 or more servers** to receive member-related events like `guild_member_add`. See the list of member-related events [under `GUILD_MEMBERS`](/developers/events/gateway#list-of-intents) |
| `1 << 15` | GATEWAY\_GUILD\_MEMBERS\_LIMITED | Intent required for bots in under 100 servers to receive member-related events like `guild_member_add`, found on the **Bot** page in your app's settings. See the list of member-related events [under `GUILD_MEMBERS`](/developers/events/gateway#list-of-intents) |
| `1 << 16` | VERIFICATION\_PENDING\_GUILD\_LIMIT | Indicates unusual growth of an app that prevents verification |
| `1 << 17` | EMBEDDED | Indicates if an app is embedded within the Discord client (currently unavailable publicly) |
| `1 << 18` | GATEWAY\_MESSAGE\_CONTENT | Intent required for bots in **100 or more servers** to receive [message content](https://support-dev.discord.com/hc/en-us/articles/4404772028055) |
| `1 << 19` | GATEWAY\_MESSAGE\_CONTENT\_LIMITED | Intent required for bots in under 100 servers to receive [message content](https://support-dev.discord.com/hc/en-us/articles/4404772028055), found on the **Bot** page in your app's settings |
| `1 << 23` | APPLICATION\_COMMAND\_BADGE | Indicates if an app has registered global [application commands](/developers/interactions/application-commands) |
### Install Params Object
###### Install Params Structure
| Field | Type | Description |
| ----------- | ---------------- | ------------------------------------------------------------------------------------------------------------ |
| scopes | array of strings | [Scopes](/developers/topics/oauth2#shared-resources-oauth2-scopes) to add the application to the server with |
| permissions | string | [Permissions](/developers/topics/permissions) to request for the bot role |
## Installation Context
An app's installation context defines how it's installed: to a server, to a user, or both.
The installation context affects how your app can be seen and used within Discord. For example, apps installed only to a user can't take actions in a server, and apps installed only to a server can't be accessed within a user's DMs.
#### Server Context
Apps installed in a server context (server-installed apps) must be authorized by a server member with the [`MANAGE_GUILD`](/developers/topics/permissions#permissions-bitwise-permission-flags) permission. Server-installed apps are *visible* to all members of the server, but other factors (like [command permissions](/developers/interactions/application-commands#permissions)) determine where and when specific members can interact with the app.
During installation, server-installed apps are authorized with a specific set of [OAuth2 scopes](/developers/topics/oauth2#shared-resources-oauth2-scopes) and [bot user permissions](/developers/topics/permissions) that determine what resources and data the app can access in that server.
#### User Context
Apps installed in a user context (user-installed apps) are visible *only* to the authorizing user, and therefore don't require any server-specific permissions.
Apps that support the user installation context are visible across all of an authorizing user's servers, DMs, and GDMs, but are forced to respect the user's permissions in the surface where the app is being used. For example, if a user invokes a command for a user-installed app from a server's channel where they don't have permission to send messages, the app won't be able to [respond to an interaction](/developers/interactions/receiving-and-responding#interaction-response-object-interaction-callback-type) with a non-ephemeral message. Details about how the installation context of a command affects interactions is in the [interaction context](/developers/interactions/application-commands#interaction-contexts) documentation.
### Setting Supported Installation Contexts
By default, newly-created apps only support installation to guilds.
You can update which installation contexts your app supports in your [app's settings](https://discord.com/developers/applications). On the [**Installation** page](https://discord.com/developers/applications/select/installation) under the **Installation Contexts** section, you can select the installation contexts your app supports.
If you update your app to support a new installation context, you will need to update your existing [commands](/developers/interactions/application-commands#contexts) if you want them to be supported in the new context. Details are in the [Application Command](/developers/interactions/application-commands#contexts) documentation.
## Install Links
Install links provide an easy way for users to install your app in Discord. If you have an install link configured, an "Add App" button will appear in your app's profile and App Directory page which will guide the user through your app's installation flow.
### Types of Install Links
There are three options when configuring an install link for your app: "Discord Provided Link", "Custom URL", and "None". If you don't configure an install link (by selecting "None"), the "Add App" button will not appear for your app, and your app will not be eligible for the App Directory.
Note that install links are distinct from OAuth2 flows like the [authorization code grant](/developers/topics/oauth2#authorization-code-grant), which may additionally be required if you need to request user-specific [scopes](/developers/topics/oauth2#shared-resources-oauth2-scopes) like `identify` or `role_connections.write`.
#### Discord Provided Link
The default Discord Provided Link is a short link that guides users through the installation flow with your app's [configured installation contexts](/developers/resources/application#setting-supported-installation-contexts). If your app has both **User Install** and **Guild Install** enabled, the user can choose which way to install your app.
Discord Provided Links don't have scopes or bot user permissions defined in the URL. For example:
```
https://discord.com/oauth2/authorize?client_id=1234567895647001626
```
Instead, these links will prompt the user for the scopes and bot user permissions configured in your Default Install Settings.
Discord Provided Links are limited to the `application.commands` and `bot` scopes
#### Custom URL
A Custom URL is an alternative to the Discord Provided Link that gives you more control of where users are directed when they click "Add App" on your app's profile or App Directory page.
A Custom URL doesn't have strict limitations, but is commonly an [OAuth2 `/authorize` URL](/developers/topics/oauth2#shared-resources-oauth2-urls) that has defined scopes, permissions, and an installation context (`integration_type`).
### Configuring an Install Link and Default Install Settings
You can configure your app's install link in your [app's settings](https://discord.com/developers/applications). On the [**Installation** page](https://discord.com/developers/applications/select/installation), go to the **Install Link** section, and select which type of install link you want for your app. For most apps, we recommend the Discord Provided Link.
The Default Install Settings will appear on the [**Installation** page](https://discord.com/developers/applications/select/installation) when you have "Discord Provided Link" selected as your install link type.
## Get Current Application
/applications/@me
Returns the [application](/developers/resources/application#application-object) object associated with the requesting bot user.
## Edit Current Application
/applications/@me
Edit properties of the app associated with the requesting bot user. Only properties that are passed will be updated. Returns the updated [application](/developers/resources/application#application-object) object on success.
All parameters to this endpoint are optional.
###### JSON Params
| Field | Type | Description |
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| custom\_install\_url | string | Default custom authorization URL for the app, if enabled |
| description | string | Description of the app |
| role\_connections\_verification\_url | string | Role connection verification URL for the app |
| install\_params | [install params](/developers/resources/application#install-params-object) object | Settings for the app's default in-app authorization link, if enabled |
| integration\_types\_config | dictionary with keys of [application integration types](/developers/resources/application#application-object-application-integration-types) | Default scopes and permissions for each supported installation context. Value for each key is an [integration type configuration object](/developers/resources/application#application-object-application-integration-type-configuration-object) |
| flags \* | integer | App's public [flags](/developers/resources/application#application-object-application-flags) |
| icon | ?[image data](/developers/reference#image-data) | Icon for the app |
| cover\_image | ?[image data](/developers/reference#image-data) | Default rich presence invite cover image for the app |
| interactions\_endpoint\_url \*\* | string | [Interactions endpoint URL](/developers/interactions/receiving-and-responding#receiving-an-interaction) for the app |
| tags | array of strings | List of tags describing the content and functionality of the app (max of 20 characters per tag). Max of 5 tags. |
| event\_webhooks\_url | string | [Event webhooks URL](/developers/events/webhook-events#preparing-for-events) for the app to receive webhook events |
| event\_webhooks\_status | [application event webhook status](/developers/resources/application#application-object-application-event-webhook-status) | If [webhook events](/developers/events/webhook-events) are enabled for the app. `1` to disable, and `2` to enable |
| event\_webhooks\_types | array of strings | List of [Webhook event types](/developers/events/webhook-events#event-types) to subscribe to |
\* Only limited intent flags (`GATEWAY_PRESENCE_LIMITED`, `GATEWAY_GUILD_MEMBERS_LIMITED`, and `GATEWAY_MESSAGE_CONTENT_LIMITED`) can be updated via the API.
\*\* To update an Interactions endpoint URL via the API, the URL must be valid according to the [Receiving an Interaction](/developers/interactions/receiving-and-responding#receiving-an-interaction) documentation.
## Get Application Activity Instance
/applications/[\{application.id}](/developers/resources/application#application-object)/activity-instances/[\{instance\_id}](/developers/resources/application#get-application-activity-instance-activity-instance-object)
Returns a serialized activity instance, if it exists. Useful for [preventing unwanted activity sessions](/developers/activities/development-guides/multiplayer-experience#preventing-unwanted-activity-sessions).
###### Example Activity Instance
```json theme={"system"}
{
"application_id": "1215413995645968394",
"instance_id": "i-1276580072400224306-gc-912952092627435520-912954213460484116",
"launch_id": "1276580072400224306",
"location": {
"id": "gc-912952092627435520-912954213460484116",
"kind": "gc",
"channel_id": "912954213460484116",
"guild_id": "912952092627435520"
},
"users": ["205519959982473217"],
}
```
###### Activity Instance Object
| Field | Type | Description |
| --------------- | ----------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| application\_id | snowflake | [Application](/developers/resources/application#application-object) ID |
| instance\_id | string | Activity [Instance](/developers/activities/development-guides/multiplayer-experience#activity-instance-management) ID |
| launch\_id | snowflake | Unique identifier for the launch |
| location | [Activity Location](/developers/resources/application#get-application-activity-instance-activity-location-object) | Location the instance is running in |
| users | array of snowflakes, [user](/developers/resources/user#user-object) IDs | IDs of the Users currently connected to the instance |
###### Activity Location Object
The Activity Location is an object that describes the location in which an activity instance is running.
| Field | Type | Description |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------- |
| id | string | Unique identifier for the location |
| kind | [Activity Location Kind Enum](/developers/resources/application#get-application-activity-instance-activity-location-kind-enum) | Enum describing kind of location |
| channel\_id | snowflake | ID of the [Channel](/developers/resources/channel#channel-object) |
| guild\_id? | ?snowflake | ID of the [Guild](/developers/resources/guild#guild-object) |
###### Activity Location Kind Enum
| Enum | Description |
| ---- | -------------------------------------------------- |
| 'gc' | Location is a Guild Channel |
| 'pc' | Location is a Private Channel, such as a DM or GDM |
# Application Role Connection Metadata
Source: https://docs.discord.com/developers/resources/application-role-connection-metadata
Complete reference for application role connection metadata objects.
A representation of role connection metadata for an [application](/developers/resources/application).
When a guild has added a bot and that bot has configured its [`role_connections_verification_url`](/developers/resources/application#application-object) (in the developer portal), the application will render as a potential verification method in the guild's role verification configuration.
If an application has configured role connection metadata, its metadata will appear in the role verification configuration when the application has been added as a verification method to the role.
When a user connects their account using the bot's [`role_connections_verification_url`](/developers/resources/application#application-object), the bot will [update a user's role connection with metadata](/developers/resources/user#update-current-user-application-role-connection) using the OAuth2 `role_connections.write` scope.
### Application Role Connection Metadata Object
###### Application Role Connection Metadata Structure
| Field | Type | Description |
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| type | [ApplicationRoleConnectionMetadataType](/developers/resources/application-role-connection-metadata#application-role-connection-metadata-object-application-role-connection-metadata-type) | type of metadata value |
| key | string | dictionary key for the metadata field (must be `a-z`, `0-9`, or `_` characters; 1-50 characters) |
| name | string | name of the metadata field (1-100 characters) |
| name\_localizations? | dictionary with keys in [available locales](/developers/reference#locales) | translations of the name |
| description | string | description of the metadata field (1-200 characters) |
| description\_localizations? | dictionary with keys in [available locales](/developers/reference#locales) | translations of the description |
###### Application Role Connection Metadata Type
| Type | Value | Description |
| ---------------------------------- | ----- | -------------------------------------------------------------------------------------------------------------------------------------- |
| INTEGER\_LESS\_THAN\_OR\_EQUAL | 1 | the metadata value (`integer`) is less than or equal to the guild's configured value (`integer`) |
| INTEGER\_GREATER\_THAN\_OR\_EQUAL | 2 | the metadata value (`integer`) is greater than or equal to the guild's configured value (`integer`) |
| INTEGER\_EQUAL | 3 | the metadata value (`integer`) is equal to the guild's configured value (`integer`) |
| INTEGER\_NOT\_EQUAL | 4 | the metadata value (`integer`) is not equal to the guild's configured value (`integer`) |
| DATETIME\_LESS\_THAN\_OR\_EQUAL | 5 | the metadata value (`ISO8601 string`) is less than or equal to the guild's configured value (`integer`; `days before current date`) |
| DATETIME\_GREATER\_THAN\_OR\_EQUAL | 6 | the metadata value (`ISO8601 string`) is greater than or equal to the guild's configured value (`integer`; `days before current date`) |
| BOOLEAN\_EQUAL | 7 | the metadata value (`integer`) is equal to the guild's configured value (`integer`; `1`) |
| BOOLEAN\_NOT\_EQUAL | 8 | the metadata value (`integer`) is not equal to the guild's configured value (`integer`; `1`) |
Each metadata type offers a comparison operation that allows guilds to configure role requirements based on metadata values stored by the bot. Bots specify a `metadata value` for each user and guilds specify the required `guild's configured value` within the guild role settings.
## Get Application Role Connection Metadata Records
/applications/[\{application.id}](/developers/resources/application#application-object)/role-connections/metadata
Returns a list of [application role connection metadata](/developers/resources/application-role-connection-metadata#application-role-connection-metadata-object) objects for the given application.
## Update Application Role Connection Metadata Records
/applications/[\{application.id}](/developers/resources/application#application-object)/role-connections/metadata
Updates and returns a list of [application role connection metadata](/developers/resources/application-role-connection-metadata#application-role-connection-metadata-object) objects for the given application.
An application can have a maximum of 5 metadata records.
# Audit Logs Resource
Source: https://docs.discord.com/developers/resources/audit-log
Reference for Discord audit log objects and endpoints.
When an administrative action is performed in a guild, an entry is added to its audit log. Viewing audit logs requires the `VIEW_AUDIT_LOG` permission and can be fetched by apps using the [`GET /guilds/{guild.id}/audit-logs` endpoint](/developers/resources/audit-log#get-guild-audit-log), or seen by users in the guild's **Server Settings**. All audit log entries are stored for 45 days.
When an app is performing an eligible action using the APIs, it can pass an `X-Audit-Log-Reason` header to indicate why the action was taken. More information is in the [audit log entry](/developers/resources/audit-log#audit-log-entry-object) section.
### Audit Log Object
###### Audit Log Structure
| Field | Type | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------- |
| application\_commands | array of [application commands](/developers/interactions/application-commands#application-command-object) objects | List of application commands referenced in the audit log |
| audit\_log\_entries | array of [audit log entry](/developers/resources/audit-log#audit-log-entry-object) objects | List of audit log entries, sorted from most to least recent |
| auto\_moderation\_rules | array of [auto moderation rule](/developers/resources/auto-moderation#auto-moderation-rule-object) objects | List of auto moderation rules referenced in the audit log |
| guild\_scheduled\_events | array of [guild scheduled event](/developers/resources/guild-scheduled-event#guild-scheduled-event-object) objects | List of guild scheduled events referenced in the audit log |
| integrations | array of partial [integration](/developers/resources/guild#integration-object) objects | List of partial integration objects |
| threads | array of thread-specific [channel](/developers/resources/channel#channel-object) objects | List of threads referenced in the audit log\* |
| users | array of [user](/developers/resources/user#user-object) objects | List of users referenced in the audit log |
| webhooks | array of [webhook](/developers/resources/webhook#webhook-object) objects | List of webhooks referenced in the audit log |
\* Threads referenced in `THREAD_CREATE` and `THREAD_UPDATE` events are included in the threads map since archived threads might not be kept in memory by clients.
###### Example Partial Integration Object
```json theme={"system"}
{
"id": "33590653072239123",
"name": "A Name",
"type": "twitch",
"account": {
"name": "twitchusername",
"id": "1234567"
},
"application_id": "94651234501213162"
}
```
### Audit Log Entry Object
Each audit log entry represents a single administrative action (or [event](/developers/resources/audit-log#audit-log-entry-object-audit-log-events)), indicated by `action_type`. Most entries contain one to many changes in the `changes` array that affected an entity in Discord—whether that's a user, channel, guild, emoji, or something else.
The information (and structure) of an entry's changes will be different depending on its type. For example, in `MEMBER_ROLE_UPDATE` events there is only one change: a member is either added or removed from a specific role. However, in `CHANNEL_CREATE` events there are many changes, including (but not limited to) the channel's name, type, and permission overwrites added. More details are in the [change object](/developers/resources/audit-log#audit-log-change-object) section.
Apps can specify why an administrative action is being taken by passing an `X-Audit-Log-Reason` request header, which will be stored as the audit log entry's `reason` field. The `X-Audit-Log-Reason` header supports 1-512 URL-encoded UTF-8 characters. Reasons are visible to users in the client and to apps when fetching audit log entries with the API.
###### Audit Log Entry Structure
| Field | Type | Description |
| ------------ | ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- |
| target\_id | ?string | ID of the affected entity (webhook, user, role, etc.) |
| changes? | array of [audit log change](/developers/resources/audit-log#audit-log-change-object) objects | Changes made to the target\_id |
| user\_id | ?snowflake | User or app that made the changes |
| id | snowflake | ID of the entry |
| action\_type | [audit log event](/developers/resources/audit-log#audit-log-entry-object-audit-log-events) | Type of action that occurred |
| options? | [optional audit entry info](/developers/resources/audit-log#audit-log-entry-object-optional-audit-entry-info) | Additional info for certain event types |
| reason? | string | Reason for the change (1-512 characters) |
For `APPLICATION_COMMAND_PERMISSION_UPDATE` events, the `target_id` is the command ID or the app ID since the `changes` array represents the entire `permissions` property on the [guild permissions](/developers/interactions/application-commands#application-command-permissions-object-guild-application-command-permissions-structure) object.
###### Audit Log Events
The table below lists audit log events and values (the `action_type` field) that your app may receive.
The **Object Changed** column notes which object's values may be included in the entry. Though there are exceptions, possible keys in the `changes` array typically correspond to the object's fields. The descriptions and types for those fields can be found in the linked documentation for the object.
If no object is noted, there won't be a `changes` array in the entry, though other fields like the `target_id` still exist and many have fields in the [`options` object](/developers/resources/audit-log#audit-log-entry-object-optional-audit-entry-info).
You should assume that your app may run into any field for the changed object, though none are guaranteed to be present. In most cases only a subset of the object's fields will be in the `changes` array.
| Event | Value | Description | Object Changed |
| ----------------------------------------------- | ----- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| GUILD\_UPDATE | 1 | Server settings were updated | [Guild](/developers/resources/guild#guild-object) |
| CHANNEL\_CREATE | 10 | Channel was created | [Channel](/developers/resources/channel#channel-object) |
| CHANNEL\_UPDATE | 11 | Channel settings were updated | [Channel](/developers/resources/channel#channel-object) |
| CHANNEL\_DELETE | 12 | Channel was deleted | [Channel](/developers/resources/channel#channel-object) |
| CHANNEL\_OVERWRITE\_CREATE | 13 | Permission overwrite was added to a channel | [Channel Overwrite](/developers/resources/channel#overwrite-object) |
| CHANNEL\_OVERWRITE\_UPDATE | 14 | Permission overwrite was updated for a channel | [Channel Overwrite](/developers/resources/channel#overwrite-object) |
| CHANNEL\_OVERWRITE\_DELETE | 15 | Permission overwrite was deleted from a channel | [Channel Overwrite](/developers/resources/channel#overwrite-object) |
| MEMBER\_KICK | 20 | Member was removed from server | |
| MEMBER\_PRUNE | 21 | Members were pruned from server | |
| MEMBER\_BAN\_ADD | 22 | Member was banned from server | |
| MEMBER\_BAN\_REMOVE | 23 | Server ban was lifted for a member | |
| MEMBER\_UPDATE | 24 | Member was updated in server | [Member](/developers/resources/guild#guild-member-object) |
| MEMBER\_ROLE\_UPDATE | 25 | Member was added or removed from a role | [Partial Role](/developers/topics/permissions#role-object)\* |
| MEMBER\_MOVE | 26 | Member was moved to a different voice channel | |
| MEMBER\_DISCONNECT | 27 | Member was disconnected from a voice channel | |
| BOT\_ADD | 28 | Bot user was added to server | |
| ROLE\_CREATE | 30 | Role was created | [Role](/developers/topics/permissions#role-object) |
| ROLE\_UPDATE | 31 | Role was edited | [Role](/developers/topics/permissions#role-object) |
| ROLE\_DELETE | 32 | Role was deleted | [Role](/developers/topics/permissions#role-object) |
| INVITE\_CREATE | 40 | Server invite was created | [Invite](/developers/resources/invite#invite-object) and [Invite Metadata](/developers/resources/invite#invite-metadata-object)\* |
| INVITE\_UPDATE | 41 | Server invite was updated | [Invite](/developers/resources/invite#invite-object) and [Invite Metadata](/developers/resources/invite#invite-metadata-object)\* |
| INVITE\_DELETE | 42 | Server invite was deleted | [Invite](/developers/resources/invite#invite-object) and [Invite Metadata](/developers/resources/invite#invite-metadata-object)\* |
| WEBHOOK\_CREATE | 50 | Webhook was created | [Webhook](/developers/resources/webhook#webhook-object)\* |
| WEBHOOK\_UPDATE | 51 | Webhook properties or channel were updated | [Webhook](/developers/resources/webhook#webhook-object)\* |
| WEBHOOK\_DELETE | 52 | Webhook was deleted | [Webhook](/developers/resources/webhook#webhook-object)\* |
| EMOJI\_CREATE | 60 | Emoji was created | [Emoji](/developers/resources/emoji#emoji-object) |
| EMOJI\_UPDATE | 61 | Emoji name was updated | [Emoji](/developers/resources/emoji#emoji-object) |
| EMOJI\_DELETE | 62 | Emoji was deleted | [Emoji](/developers/resources/emoji#emoji-object) |
| MESSAGE\_DELETE | 72 | Single message was deleted | |
| MESSAGE\_BULK\_DELETE | 73 | Multiple messages were deleted | |
| MESSAGE\_PIN | 74 | Message was pinned to a channel | |
| MESSAGE\_UNPIN | 75 | Message was unpinned from a channel | |
| INTEGRATION\_CREATE | 80 | App was added to server | [Integration](/developers/resources/guild#integration-object) |
| INTEGRATION\_UPDATE | 81 | App was updated (as an example, its scopes were updated) | [Integration](/developers/resources/guild#integration-object) |
| INTEGRATION\_DELETE | 82 | App was removed from server | [Integration](/developers/resources/guild#integration-object) |
| STAGE\_INSTANCE\_CREATE | 83 | Stage instance was created (stage channel becomes live) | [Stage Instance](/developers/resources/stage-instance#stage-instance-object) |
| STAGE\_INSTANCE\_UPDATE | 84 | Stage instance details were updated | [Stage Instance](/developers/resources/stage-instance#stage-instance-object) |
| STAGE\_INSTANCE\_DELETE | 85 | Stage instance was deleted (stage channel no longer live) | [Stage Instance](/developers/resources/stage-instance#stage-instance-object) |
| STICKER\_CREATE | 90 | Sticker was created | [Sticker](/developers/resources/sticker#sticker-object) |
| STICKER\_UPDATE | 91 | Sticker details were updated | [Sticker](/developers/resources/sticker#sticker-object) |
| STICKER\_DELETE | 92 | Sticker was deleted | [Sticker](/developers/resources/sticker#sticker-object) |
| GUILD\_SCHEDULED\_EVENT\_CREATE | 100 | Event was created | [Guild Scheduled Event](/developers/resources/guild-scheduled-event#guild-scheduled-event-object) |
| GUILD\_SCHEDULED\_EVENT\_UPDATE | 101 | Event was updated | [Guild Scheduled Event](/developers/resources/guild-scheduled-event#guild-scheduled-event-object) |
| GUILD\_SCHEDULED\_EVENT\_DELETE | 102 | Event was cancelled | [Guild Scheduled Event](/developers/resources/guild-scheduled-event#guild-scheduled-event-object) |
| THREAD\_CREATE | 110 | Thread was created in a channel | [Thread](/developers/resources/channel#thread-metadata-object) |
| THREAD\_UPDATE | 111 | Thread was updated | [Thread](/developers/resources/channel#thread-metadata-object) |
| THREAD\_DELETE | 112 | Thread was deleted | [Thread](/developers/resources/channel#thread-metadata-object) |
| APPLICATION\_COMMAND\_PERMISSION\_UPDATE | 121 | Permissions were updated for a command | [Command Permission](/developers/interactions/application-commands#application-command-permissions-object-application-command-permissions-structure)\* |
| SOUNDBOARD\_SOUND\_CREATE | 130 | Soundboard sound was created | [Soundboard Sound](/developers/resources/soundboard#soundboard-sound-object) |
| SOUNDBOARD\_SOUND\_UPDATE | 131 | Soundboard sound was updated | [Soundboard Sound](/developers/resources/soundboard#soundboard-sound-object) |
| SOUNDBOARD\_SOUND\_DELETE | 132 | Soundboard sound was deleted | [Soundboard Sound](/developers/resources/soundboard#soundboard-sound-object) |
| AUTO\_MODERATION\_RULE\_CREATE | 140 | Auto Moderation rule was created | [Auto Moderation Rule](/developers/resources/auto-moderation#auto-moderation-rule-object) |
| AUTO\_MODERATION\_RULE\_UPDATE | 141 | Auto Moderation rule was updated | [Auto Moderation Rule](/developers/resources/auto-moderation#auto-moderation-rule-object) |
| AUTO\_MODERATION\_RULE\_DELETE | 142 | Auto Moderation rule was deleted | [Auto Moderation Rule](/developers/resources/auto-moderation#auto-moderation-rule-object) |
| AUTO\_MODERATION\_BLOCK\_MESSAGE | 143 | Message was blocked by Auto Moderation | |
| AUTO\_MODERATION\_FLAG\_TO\_CHANNEL | 144 | Message was flagged by Auto Moderation | |
| AUTO\_MODERATION\_USER\_COMMUNICATION\_DISABLED | 145 | Member was timed out by Auto Moderation | |
| AUTO\_MODERATION\_QUARANTINE\_USER | 146 | Member was quarantined by Auto Moderation | |
| CREATOR\_MONETIZATION\_REQUEST\_CREATED | 150 | Creator monetization request was created | |
| CREATOR\_MONETIZATION\_TERMS\_ACCEPTED | 151 | Creator monetization terms were accepted | |
| ONBOARDING\_PROMPT\_CREATE | 163 | Guild Onboarding Question was created | [Onboarding Prompt Structure](/developers/resources/guild#guild-onboarding-object-onboarding-prompt-structure) |
| ONBOARDING\_PROMPT\_UPDATE | 164 | Guild Onboarding Question was updated | [Onboarding Prompt Structure](/developers/resources/guild#guild-onboarding-object-onboarding-prompt-structure) |
| ONBOARDING\_PROMPT\_DELETE | 165 | Guild Onboarding Question was deleted | [Onboarding Prompt Structure](/developers/resources/guild#guild-onboarding-object-onboarding-prompt-structure) |
| ONBOARDING\_CREATE | 166 | Guild Onboarding was created | [Guild Onboarding](/developers/resources/guild#guild-onboarding-object) |
| ONBOARDING\_UPDATE | 167 | Guild Onboarding was updated | [Guild Onboarding](/developers/resources/guild#guild-onboarding-object) |
| HOME\_SETTINGS\_CREATE | 190 | Guild Server Guide was created | |
| HOME\_SETTINGS\_UPDATE | 191 | Guild Server Guide was updated | |
| VOICE\_CHANNEL\_STATUS\_CREATE | 192 | A voice channel status was set by a user | |
| VOICE\_CHANNEL\_STATUS\_DELETE | 193 | A voice channel status was deleted by a user | |
\* Object has exception(s) to available keys. See the [exceptions](/developers/resources/audit-log#audit-log-change-object-audit-log-change-exceptions) section below for details.
###### Optional Audit Entry Info
| Field | Type | Description | Event Types |
| ------------------------------------- | --------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| application\_id | snowflake | ID of the app whose permissions were targeted | APPLICATION\_COMMAND\_PERMISSION\_UPDATE |
| auto\_moderation\_rule\_name | string | Name of the Auto Moderation rule that was triggered | AUTO\_MODERATION\_BLOCK\_MESSAGE & AUTO\_MODERATION\_FLAG\_TO\_CHANNEL & AUTO\_MODERATION\_USER\_COMMUNICATION\_DISABLED & AUTO\_MODERATION\_QUARANTINE\_USER |
| auto\_moderation\_rule\_trigger\_type | string | Trigger type of the Auto Moderation rule that was triggered | AUTO\_MODERATION\_BLOCK\_MESSAGE & AUTO\_MODERATION\_FLAG\_TO\_CHANNEL & AUTO\_MODERATION\_USER\_COMMUNICATION\_DISABLED & AUTO\_MODERATION\_QUARANTINE\_USER |
| channel\_id | snowflake | Channel in which the entities were targeted | MEMBER\_MOVE & MESSAGE\_PIN & MESSAGE\_UNPIN & MESSAGE\_DELETE & STAGE\_INSTANCE\_CREATE & STAGE\_INSTANCE\_UPDATE & STAGE\_INSTANCE\_DELETE & AUTO\_MODERATION\_BLOCK\_MESSAGE & AUTO\_MODERATION\_FLAG\_TO\_CHANNEL & AUTO\_MODERATION\_USER\_COMMUNICATION\_DISABLED & AUTO\_MODERATION\_QUARANTINE\_USER & VOICE\_CHANNEL\_STATUS\_CREATE & VOICE\_CHANNEL\_STATUS\_DELETE |
| count | string | Number of entities that were targeted | MESSAGE\_DELETE & MESSAGE\_BULK\_DELETE & MEMBER\_DISCONNECT & MEMBER\_MOVE |
| delete\_member\_days | string | Number of days after which inactive members were kicked | MEMBER\_PRUNE |
| id | snowflake | ID of the overwritten entity | CHANNEL\_OVERWRITE\_CREATE & CHANNEL\_OVERWRITE\_UPDATE & CHANNEL\_OVERWRITE\_DELETE |
| members\_removed | string | Number of members removed by the prune | MEMBER\_PRUNE |
| message\_id | snowflake | ID of the message that was targeted | MESSAGE\_PIN & MESSAGE\_UNPIN |
| role\_name | string | Name of the role if type is `"0"` (not present if type is `"1"`) | CHANNEL\_OVERWRITE\_CREATE & CHANNEL\_OVERWRITE\_UPDATE & CHANNEL\_OVERWRITE\_DELETE |
| type | string | Type of overwritten entity - role (`"0"`) or member (`"1"`) | CHANNEL\_OVERWRITE\_CREATE & CHANNEL\_OVERWRITE\_UPDATE & CHANNEL\_OVERWRITE\_DELETE |
| integration\_type | string | The type of integration which performed the action | MEMBER\_KICK & MEMBER\_ROLE\_UPDATE |
| status | string | The new voice channel status | VOICE\_CHANNEL\_STATUS\_CREATE |
### Audit Log Change Object
Many audit log events include a `changes` array in their [entry object](/developers/resources/audit-log#audit-log-entry-object-audit-log-entry-structure). The [structure for the individual changes](/developers/resources/audit-log#audit-log-change-object-audit-log-change-structure) varies based on the event type and its changed objects, so apps shouldn't depend on a single pattern of handling audit log events.
###### Audit Log Change Structure
Some events don't follow the same pattern as other audit log events. Details about these exceptions are explained in [the next section](/developers/resources/audit-log#audit-log-change-object-audit-log-change-exceptions).
If `new_value` is not present in the change object while `old_value` is, it indicates that the property has been reset or set to `null`. If `old_value` isn't included, it indicated that the property was previously `null`.
| Field | Type | Description |
| ----------- | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| new\_value? | mixed (matches object field's type) | New value of the key |
| old\_value? | mixed (matches object field's type) | Old value of the key |
| key | string | Name of the changed entity, with a few [exceptions](/developers/resources/audit-log#audit-log-change-object-audit-log-change-exceptions) |
###### Audit Log Change Exceptions
For most objects, the change keys may be any field on the changed object. The following table details the exceptions to this pattern.
| Object Changed | Change Key Exceptions | Change Object Exceptions |
| ---------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Command Permission](/developers/interactions/application-commands#application-command-permissions-object-application-command-permissions-structure) | snowflake as key | The `changes` array contains objects with a `key` field representing the entity whose command was affected (role, channel, or user ID), a previous permissions object (with an `old_value` key), and an updated permissions object (with a `new_value` key) |
| [Invite](/developers/resources/invite#invite-object) and [Invite Metadata](/developers/resources/invite#invite-metadata-object) | Additional `channel_id` key (instead of object's `channel.id`) | |
| [Partial Role](/developers/topics/permissions#role-object) | `$add` and `$remove` as keys | `new_value` is an array of objects that contain the role `id` and `name` |
| [Webhook](/developers/resources/webhook#webhook-object) | `avatar_hash` key (instead of `avatar`) | |
## Get Guild Audit Log
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/audit-logs
Returns an [audit log](/developers/resources/audit-log#audit-log-object) object for the guild. Requires the [`VIEW_AUDIT_LOG`](/developers/topics/permissions#permissions-bitwise-permission-flags) permission.
The returned list of audit log entries is ordered based on whether you use `before` or `after`. When using `before`, the list is ordered by the audit log entry ID **descending** (newer entries first). If `after` is used, the list is reversed and appears in **ascending** order (older entries first). Omitting both `before` and `after` defaults to `before` the current timestamp and will show the most recent entries in descending order by ID, the opposite can be achieved using `after=0` (showing oldest entries).
###### Query String Params
The following parameters can be used to filter which and how many audit log entries are returned.
| Field | Type | Description |
| ------------- | --------- | ----------------------------------------------------------------------------------------------------------------- |
| user\_id? | snowflake | Entries from a specific user ID |
| action\_type? | integer | Entries for a specific [audit log event](/developers/resources/audit-log#audit-log-entry-object-audit-log-events) |
| before? | snowflake | Entries with ID less than a specific audit log entry ID |
| after? | snowflake | Entries with ID greater than a specific audit log entry ID |
| limit? | integer | Maximum number of entries (between 1-100) to return, defaults to 50 |
# Auto Moderation
Source: https://docs.discord.com/developers/resources/auto-moderation
Reference for Discord's Auto Moderation system and endpoints.
Auto Moderation is a feature which allows each [guild](/developers/resources/guild) to set up rules that trigger based on some criteria. For example, a rule can trigger whenever a message contains a specific keyword.
Rules can be configured to automatically execute actions whenever they trigger. For example, if a user tries to send a message which contains a certain keyword, a rule can trigger and block the message before it is sent.
### Auto Moderation Rule Object
###### Auto Moderation Rule Structure
| Field | Type | Description |
| ----------------- | ---------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| id | snowflake | the id of this rule |
| guild\_id | snowflake | the id of the guild which this rule belongs to |
| name | string | the rule name |
| creator\_id | snowflake | the user which first created this rule |
| event\_type | integer | the rule [event type](/developers/resources/auto-moderation#auto-moderation-rule-object-event-types) |
| trigger\_type | integer | the rule [trigger type](/developers/resources/auto-moderation#auto-moderation-rule-object-trigger-types) |
| trigger\_metadata | object | the rule [trigger metadata](/developers/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata) |
| actions | array of [action](/developers/resources/auto-moderation#auto-moderation-action-object) objects | the actions which will execute when the rule is triggered |
| enabled | boolean | whether the rule is enabled |
| exempt\_roles | array of snowflakes | the role ids that should not be affected by the rule (Maximum of 20) |
| exempt\_channels | array of snowflakes | the channel ids that should not be affected by the rule (Maximum of 50) |
###### Example Auto Moderation Rule
```json theme={"system"}
{
"id": "969707018069872670",
"guild_id": "613425648685547541",
"name": "Keyword Filter 1",
"creator_id": "423457898095789043",
"trigger_type": 1,
"event_type": 1,
"actions": [
{
"type": 1,
"metadata": { "custom_message": "Please keep financial discussions limited to the #finance channel" }
},
{
"type": 2,
"metadata": { "channel_id": "123456789123456789" }
},
{
"type": 3,
"metadata": { "duration_seconds": 60 }
}
],
"trigger_metadata": {
"keyword_filter": ["cat*", "*dog", "*ana*", "i like c++"],
"regex_patterns": ["(b|c)at", "^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}$"]
},
"enabled": true,
"exempt_roles": ["323456789123456789", "423456789123456789"],
"exempt_channels": ["523456789123456789"]
}
```
###### Trigger Types
Characterizes the type of content which can trigger the rule.
| Trigger Type | Value | Description | Max per Guild |
| --------------- | ----- | --------------------------------------------------------------------------- | ------------- |
| KEYWORD | 1 | check if content contains words from a user defined list of keywords | 6 |
| SPAM | 3 | check if content represents generic spam | 1 |
| KEYWORD\_PRESET | 4 | check if content contains words from internal pre-defined wordsets | 1 |
| MENTION\_SPAM | 5 | check if content contains more unique mentions than allowed | 1 |
| MEMBER\_PROFILE | 6 | check if member profile contains words from a user defined list of keywords | 1 |
###### Trigger Metadata
Additional data used to determine whether a rule should be triggered. Different fields are relevant based on the
value of [trigger\_type](/developers/resources/auto-moderation#auto-moderation-rule-object-trigger-types).
| Field | Type | Associated Trigger Types | Description |
| ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------- | --------------------------------------------------------------------------------- |
| keyword\_filter | array of strings \* | KEYWORD, MEMBER\_PROFILE | substrings which will be searched for in content (Maximum of 1000) |
| regex\_patterns | array of strings \*\* | KEYWORD, MEMBER\_PROFILE | regular expression patterns which will be matched against content (Maximum of 10) |
| presets | array of [keyword preset types](/developers/resources/auto-moderation#auto-moderation-rule-object-keyword-preset-types) | KEYWORD\_PRESET | the internally pre-defined wordsets which will be searched for in content |
| allow\_list | array of strings \*\*\* | KEYWORD, KEYWORD\_PRESET, MEMBER\_PROFILE | substrings which should not trigger the rule (Maximum of 100 or 1000) |
| mention\_total\_limit | integer | MENTION\_SPAM | total number of unique role and user mentions allowed per message (Maximum of 50) |
| mention\_raid\_protection\_enabled | boolean | MENTION\_SPAM | whether to automatically detect mention raids |
\* A keyword can be a phrase which contains multiple words. [Wildcard symbols](/developers/resources/auto-moderation#auto-moderation-rule-object-keyword-matching-strategies) can be used to customize how each keyword will be matched. Each keyword must be 60 characters or less.
\*\* Only Rust flavored regex is currently supported, which can be tested in online editors such as [Rustexp](https://rustexp.lpil.uk/). Each regex pattern must be 260 characters or less.
\*\*\* Each `allow_list` keyword can be a phrase which contains multiple words. [Wildcard symbols](/developers/resources/auto-moderation#auto-moderation-rule-object-keyword-matching-strategies) can be used to customize how each keyword will be matched. Rules with `KEYWORD` [trigger\_type](/developers/resources/auto-moderation#auto-moderation-rule-object-trigger-types) accept a maximum of 100 keywords. Rules with `KEYWORD_PRESET` [trigger\_type](/developers/resources/auto-moderation#auto-moderation-rule-object-trigger-types) accept a maximum of 1000 keywords.
###### Trigger Metadata Field Limits
| Field | Trigger Types | MAX ARRAY LENGTH | MAX CHARACTERS PER STRING |
| --------------- | ------------------------ | ---------------- | ------------------------- |
| keyword\_filter | KEYWORD, MEMBER\_PROFILE | 1000 | 60 |
| regex\_patterns | KEYWORD, MEMBER\_PROFILE | 10 | 260 |
| allow\_list | KEYWORD, MEMBER\_PROFILE | 100 | 60 |
| allow\_list | KEYWORD\_PRESET | 1000 | 60 |
###### Keyword Preset Types
| Preset Type | Value | Description |
| --------------- | ----- | ------------------------------------------------------------ |
| PROFANITY | 1 | words that may be considered forms of swearing or cursing |
| SEXUAL\_CONTENT | 2 | words that refer to sexually explicit behavior or activity |
| SLURS | 3 | personal insults or words that may be considered hate speech |
###### Event Types
Indicates in what event context a rule should be checked.
| Event Type | Value | Description |
| -------------- | ----- | --------------------------------------------------- |
| MESSAGE\_SEND | 1 | when a member sends or edits a message in the guild |
| MEMBER\_UPDATE | 2 | when a member edits their profile |
###### Keyword Matching Strategies
Use the wildcard symbol (`*`) at the beginning or end of a keyword to define how it should be matched. All keywords are case insensitive.
**Prefix** - word must start with the keyword
| Keyword | Matches |
| --------- | ------------------------------------- |
| cat\* | **cat**ch, **Cat**apult, **CAt**tLE |
| tra\* | **tra**in, **tra**de, **TRA**ditional |
| the mat\* | **the mat**rix |
**Suffix** - word must end with the keyword
| Keyword | Matches |
| --------- | ----------------------------------- |
| \*cat | wild**cat**, copy**Cat** |
| \*tra | ex**tra**, ul**tra**, orches**TRA** |
| \*the mat | brea**the mat** |
**Anywhere** - keyword can appear anywhere in the content
| Keyword | Matches |
| ----------- | --------------------------- |
| \*cat\* | lo**cat**ion, edu**Cat**ion |
| \*tra\* | abs**tra**cted, ou**tra**ge |
| \*the mat\* | brea**the mat**ter |
**Whole Word** - keyword is a full word or phrase and must be surrounded by whitespace
| Keyword | Matches |
| ------- | ----------- |
| cat | **cat** |
| train | **train** |
| the mat | **the mat** |
### Auto Moderation Action Object
An action which will execute whenever a rule is triggered.
###### Auto Moderation Action Structure
| Field | Type | Description |
| ------------ | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------- |
| type | [action type](/developers/resources/auto-moderation#auto-moderation-action-object-action-types) | the type of action |
| metadata? \* | [action metadata](/developers/resources/auto-moderation#auto-moderation-action-object-action-metadata) | additional metadata needed during execution for this specific action type |
\* Can be omitted based on `type`. See the `Associated Action Types` column in [action metadata](/developers/resources/auto-moderation#auto-moderation-action-object-action-metadata) to understand which `type` values require `metadata` to be set.
###### Action Types
| Action Type | Value | Description |
| -------------------------- | ----- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| BLOCK\_MESSAGE | 1 | blocks a member's message and prevents it from being posted. A custom explanation can be specified and shown to members whenever their message is blocked. |
| SEND\_ALERT\_MESSAGE | 2 | logs user content to a specified channel |
| TIMEOUT | 3 | timeout user for a specified duration \* |
| BLOCK\_MEMBER\_INTERACTION | 4 | prevents a member from using text, voice, or other interactions |
\* A `TIMEOUT` action can only be set up for `KEYWORD` and `MENTION_SPAM` rules. The `MODERATE_MEMBERS` permission is required to use the `TIMEOUT` action type.
###### Action Metadata
Additional data used when an action is executed. Different fields are relevant based on the
value of [action type](/developers/resources/auto-moderation#auto-moderation-action-object-action-types).
| Field | Type | Associated Action Types | Description | Constraints |
| ----------------- | --------- | ----------------------- | -------------------------------------------------------------------------------------- | ------------------------------------ |
| channel\_id | snowflake | SEND\_ALERT\_MESSAGE | channel to which user content should be logged | existing channel |
| duration\_seconds | integer | TIMEOUT | timeout duration in seconds | maximum of 2419200 seconds (4 weeks) |
| custom\_message? | string | BLOCK\_MESSAGE | additional explanation that will be shown to members whenever their message is blocked | maximum of 150 characters |
### Auto Moderation Permission Requirements
Users are required to have the `MANAGE_GUILD` permission to access all Auto Moderation resources.
Some [action types](/developers/resources/auto-moderation#auto-moderation-action-object-action-types) require additional permissions, e.g. the `TIMEOUT` action type requires an additional `MODERATE_MEMBERS` permission.
## List Auto Moderation Rules for Guild
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/auto-moderation/rules
Get a list of all rules currently configured for the guild. Returns a list of [auto moderation rule](/developers/resources/auto-moderation#auto-moderation-rule-object) objects for the given guild.
This endpoint requires the `MANAGE_GUILD` [permission](/developers/resources/auto-moderation#auto-moderation-permission-requirements).
## Get Auto Moderation Rule
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/auto-moderation/rules/[\{auto\_moderation\_rule.id}](/developers/resources/auto-moderation#auto-moderation-rule-object)
Get a single rule. Returns an [auto moderation rule](/developers/resources/auto-moderation#auto-moderation-rule-object) object.
This endpoint requires the `MANAGE_GUILD` [permission](/developers/resources/auto-moderation#auto-moderation-permission-requirements).
## Create Auto Moderation Rule
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/auto-moderation/rules
Create a new rule. Returns an [auto moderation rule](/developers/resources/auto-moderation#auto-moderation-rule-object) on success. Fires an [Auto Moderation Rule Create](/developers/events/gateway-events#auto-moderation-rule-create) Gateway event.
This endpoint requires the `MANAGE_GUILD` [permission](/developers/resources/auto-moderation#auto-moderation-permission-requirements).
This endpoint supports the `X-Audit-Log-Reason` header.
###### JSON Params
| Field | Type | Description |
| --------------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| name | string | the rule name |
| event\_type | integer | the [event type](/developers/resources/auto-moderation#auto-moderation-rule-object-event-types) |
| trigger\_type | integer | the [trigger type](/developers/resources/auto-moderation#auto-moderation-rule-object-trigger-types) |
| trigger\_metadata? \* | object | the [trigger metadata](/developers/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata) |
| actions | array of [action](/developers/resources/auto-moderation#auto-moderation-action-object) objects | the actions which will execute when the rule is triggered |
| enabled? | boolean | whether the rule is enabled (False by default) |
| exempt\_roles? | array of snowflakes | the role ids that should not be affected by the rule (Maximum of 20) |
| exempt\_channels? | array of snowflakes | the channel ids that should not be affected by the rule (Maximum of 50) |
\* Can be omitted based on `trigger_type`. See the `Associated Trigger Types` column in [trigger metadata](/developers/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata) to understand which `trigger_type` values require `trigger_metadata` to be set.
See [Trigger Types](/developers/resources/auto-moderation#auto-moderation-rule-object-trigger-types) for limits on how many rules of each trigger type can be created per guild.
## Modify Auto Moderation Rule
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/auto-moderation/rules/[\{auto\_moderation\_rule.id}](/developers/resources/auto-moderation#auto-moderation-rule-object)
Modify an existing rule. Returns an [auto moderation rule](/developers/resources/auto-moderation#auto-moderation-rule-object) on success. Fires an [Auto Moderation Rule Update](/developers/events/gateway-events#auto-moderation-rule-update) Gateway event.
Requires `MANAGE_GUILD` [permissions](/developers/resources/auto-moderation#auto-moderation-permission-requirements).
All parameters for this endpoint are optional.
This endpoint supports the `X-Audit-Log-Reason` header.
###### JSON Params
| Field | Type | Description |
| --------------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| name | string | the rule name |
| event\_type | integer | the [event type](/developers/resources/auto-moderation#auto-moderation-rule-object-event-types) |
| trigger\_metadata? \* | object | the [trigger metadata](/developers/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata) |
| actions | array of [action](/developers/resources/auto-moderation#auto-moderation-action-object) objects | the actions which will execute when the rule is triggered |
| enabled | boolean | whether the rule is enabled |
| exempt\_roles | array of snowflakes | the role ids that should not be affected by the rule (Maximum of 20) |
| exempt\_channels | array of snowflakes | the channel ids that should not be affected by the rule (Maximum of 50) |
\* Can be omitted based on `trigger_type`. See the `Associated Trigger Types` column in [trigger metadata](/developers/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata) to understand which `trigger_type` values require `trigger_metadata` to be set.
## Delete Auto Moderation Rule
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/auto-moderation/rules/[\{auto\_moderation\_rule.id}](/developers/resources/auto-moderation#auto-moderation-rule-object)
Delete a rule. Returns a `204` on success. Fires an [Auto Moderation Rule Delete](/developers/events/gateway-events#auto-moderation-rule-delete) Gateway event.
This endpoint requires the `MANAGE_GUILD` [permission](/developers/resources/auto-moderation#auto-moderation-permission-requirements).
This endpoint supports the `X-Audit-Log-Reason` header.
# Channels Resource
Source: https://docs.discord.com/developers/resources/channel
### Channel Object
Represents a guild or DM channel within Discord.
###### Channel Structure
| Field | Type | Description |
| ---------------------------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| id | snowflake | the id of this channel |
| type | integer | the [type of channel](/developers/resources/channel#channel-object-channel-types) |
| guild\_id? | snowflake | the id of the guild (may be missing for some channel objects received over gateway guild dispatches) |
| position? | integer | sorting position of the channel (channels with the same position are sorted by id) |
| permission\_overwrites? | array of [overwrite](/developers/resources/channel#overwrite-object) objects | explicit permission overwrites for members and roles |
| name? | ?string | the name of the channel (1-100 characters) |
| topic? | ?string | the channel topic (0-4096 characters for `GUILD_FORUM` and `GUILD_MEDIA` channels, 0-1024 characters for all others) |
| nsfw? | boolean | whether the channel is [age-restricted](https://support.discord.com/hc/en-us/articles/115000084051) |
| last\_message\_id? | ?snowflake | the id of the last message sent in this channel (or thread for `GUILD_FORUM` or `GUILD_MEDIA` channels) (may not point to an existing or valid message or thread) |
| bitrate? | integer | the bitrate (in bits per second) of the voice channel |
| user\_limit? | integer | the user limit of the voice channel |
| rate\_limit\_per\_user?\* | integer | amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission `BYPASS_SLOWMODE`, are unaffected |
| recipients? | array of [user](/developers/resources/user#user-object) objects | the recipients of the DM |
| icon? | ?string | icon hash of the group DM |
| owner\_id? | snowflake | id of the creator of the group DM or thread |
| application\_id? | snowflake | application id of the group DM creator if it is bot-created |
| managed? | boolean | for group DM channels: whether the channel is managed by an application via the `gdm.join` OAuth2 scope |
| parent\_id? | ?snowflake | for guild channels: id of the parent category for a channel (each parent category can contain up to 50 channels), for threads: id of the text channel this thread was created |
| last\_pin\_timestamp? | ?ISO8601 timestamp | when the last pinned message was pinned. This may be `null` in events such as `GUILD_CREATE` when a message is not pinned. |
| rtc\_region? | ?string | [voice region](/developers/resources/voice#voice-region-object) id for the voice channel, automatic when set to null |
| video\_quality\_mode? | integer | the camera [video quality mode](/developers/resources/channel#channel-object-video-quality-modes) of the voice channel, 1 when not present |
| message\_count?\*\* | integer | number of messages (not including the initial message or deleted messages) in a thread. |
| member\_count? | integer | an approximate count of users in a thread, stops counting at 50 |
| thread\_metadata? | a [thread metadata](/developers/resources/channel#thread-metadata-object) object | thread-specific fields not needed by other channels |
| member? | a [thread member](/developers/resources/channel#thread-member-object) object | thread member object for the current user, if they have joined the thread, only included on certain API endpoints |
| default\_auto\_archive\_duration? | integer | default duration, copied onto newly created threads, in minutes, threads will stop showing in the channel list after the specified period of inactivity, can be set to: 60, 1440, 4320, 10080 |
| permissions? | string | computed permissions for the invoking user in the channel, including overwrites, only included when part of the `resolved` data received on an interaction. This does not include [implicit permissions](/developers/topics/permissions#implicit-permissions), which may need to be checked separately |
| flags? | integer | [channel flags](/developers/resources/channel#channel-object-channel-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) |
| total\_message\_sent? | integer | number of messages ever sent in a thread, it's similar to `message_count` on message creation, but will not decrement the number when a message is deleted |
| available\_tags? | array of [tag](/developers/resources/channel#forum-tag-object) objects | the set of tags that can be used in a `GUILD_FORUM` or a `GUILD_MEDIA` channel |
| applied\_tags? | array of snowflakes | the IDs of the set of tags that have been applied to a thread in a `GUILD_FORUM` or a `GUILD_MEDIA` channel |
| default\_reaction\_emoji? | ?[default reaction](/developers/resources/channel#default-reaction-object) object | the emoji to show in the add reaction button on a thread in a `GUILD_FORUM` or a `GUILD_MEDIA` channel |
| default\_thread\_rate\_limit\_per\_user? | integer | the initial `rate_limit_per_user` to set on newly created threads in a channel. this field is copied to the thread at creation time and does not live update. |
| default\_sort\_order? | ?integer | the [default sort order type](/developers/resources/channel#channel-object-sort-order-types) used to order posts in `GUILD_FORUM` and `GUILD_MEDIA` channels. Defaults to `null`, which indicates a preferred sort order hasn't been set by a channel admin |
| default\_forum\_layout? | integer | the [default forum layout view](/developers/resources/channel#channel-object-forum-layout-types) used to display posts in `GUILD_FORUM` channels. Defaults to `0`, which indicates a layout view has not been set by a channel admin |
\* `rate_limit_per_user` also applies to thread creation. Users can send one message and create one thread during each `rate_limit_per_user` interval.
\*\* For threads created before July 1, 2022, the message count is inaccurate when it's greater than 50.
###### Channel Types
Type 10, 11 and 12 are only available in API v9 and above.
| Type | ID | Description |
| -------------------- | -- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| GUILD\_TEXT | 0 | a text channel within a server |
| DM | 1 | a direct message between users |
| GUILD\_VOICE | 2 | a voice channel within a server |
| GROUP\_DM | 3 | a direct message between multiple users |
| GUILD\_CATEGORY | 4 | an [organizational category](https://support.discord.com/hc/en-us/articles/115001580171-Channel-Categories-101) that contains up to 50 channels |
| GUILD\_ANNOUNCEMENT | 5 | a channel that [users can follow and crosspost into their own server](https://support.discord.com/hc/en-us/articles/360032008192) (formerly news channels) |
| ANNOUNCEMENT\_THREAD | 10 | a temporary sub-channel within a GUILD\_ANNOUNCEMENT channel |
| PUBLIC\_THREAD | 11 | a temporary sub-channel within a GUILD\_TEXT or GUILD\_FORUM channel |
| PRIVATE\_THREAD | 12 | a temporary sub-channel within a GUILD\_TEXT channel that is only viewable by those invited and those with the MANAGE\_THREADS permission |
| GUILD\_STAGE\_VOICE | 13 | a voice channel for [hosting events with an audience](https://support.discord.com/hc/en-us/articles/1500005513722) |
| GUILD\_DIRECTORY | 14 | the channel in a [hub](https://support.discord.com/hc/en-us/articles/4406046651927-Discord-Student-Hubs-FAQ) containing the listed servers |
| GUILD\_FORUM | 15 | Channel that can only contain threads |
| GUILD\_MEDIA | 16 | Channel that can only contain threads, similar to `GUILD_FORUM` channels |
\* The `GUILD_MEDIA` channel type is still in active development. Avoid implementing any features that are not documented here, since they are subject to change without notice!
###### Video Quality Modes
| Mode | Value | Description |
| ---- | ----- | --------------------------------------------------- |
| AUTO | 1 | Discord chooses the quality for optimal performance |
| FULL | 2 | 720p |
###### Channel Flags
| Flag | Value | Description |
| ------------------------------ | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| PINNED | `1 << 1` | this thread is pinned to the top of its parent `GUILD_FORUM` or `GUILD_MEDIA` channel |
| REQUIRE\_TAG | `1 << 4` | whether a tag is required to be specified when creating a thread in a `GUILD_FORUM` or a `GUILD_MEDIA` channel. Tags are specified in the `applied_tags` field. |
| HIDE\_MEDIA\_DOWNLOAD\_OPTIONS | `1 << 15` | when set hides the embedded media download options. Available only for media channels |
###### Sort Order Types
| Flag | Value | Description |
| ---------------- | ----- | -------------------------------------------------------------- |
| LATEST\_ACTIVITY | 0 | Sort forum posts by activity |
| CREATION\_DATE | 1 | Sort forum posts by creation time (from most recent to oldest) |
###### Forum Layout Types
| Flag | Value | Description |
| ------------- | ----- | ----------------------------------------- |
| NOT\_SET | 0 | No default has been set for forum channel |
| LIST\_VIEW | 1 | Display posts as a list |
| GALLERY\_VIEW | 2 | Display posts as a collection of tiles |
###### Example Guild Text Channel
```json theme={"system"}
{
"id": "41771983423143937",
"guild_id": "41771983423143937",
"name": "general",
"type": 0,
"position": 6,
"permission_overwrites": [],
"rate_limit_per_user": 2,
"nsfw": true,
"topic": "24/7 chat about how to gank Mike #2",
"last_message_id": "155117677105512449",
"parent_id": "399942396007890945",
"default_auto_archive_duration": 60
}
```
###### Example Guild Announcement Channel
Bots can post or publish messages in this type of channel if they have the proper permissions.
```json theme={"system"}
{
"id": "41771983423143937",
"guild_id": "41771983423143937",
"name": "important-news",
"type": 5,
"position": 6,
"permission_overwrites": [],
"nsfw": true,
"topic": "Rumors about Half Life 3",
"last_message_id": "155117677105512449",
"parent_id": "399942396007890945",
"default_auto_archive_duration": 60
}
```
###### Example Guild Voice Channel
```json theme={"system"}
{
"id": "155101607195836416",
"last_message_id": "174629835082649376",
"type": 2,
"name": "ROCKET CHEESE",
"position": 5,
"parent_id": null,
"bitrate": 64000,
"user_limit": 0,
"rtc_region": null,
"guild_id": "41771983423143937",
"permission_overwrites": [],
"rate_limit_per_user": 0,
"nsfw": false,
}
```
###### Example DM Channel
```json theme={"system"}
{
"last_message_id": "3343820033257021450",
"type": 1,
"id": "319674150115610528",
"recipients": [
{
"username": "test",
"discriminator": "9999",
"id": "82198898841029460",
"avatar": "33ecab261d4681afa4d85a04691c4a01"
}
]
}
```
###### Example Group DM Channel
```json theme={"system"}
{
"name": "Some test channel",
"icon": null,
"recipients": [
{
"username": "test",
"discriminator": "9999",
"id": "82198898841029460",
"avatar": "33ecab261d4681afa4d85a04691c4a01"
},
{
"username": "test2",
"discriminator": "9999",
"id": "82198810841029460",
"avatar": "33ecab261d4681afa4d85a10691c4a01"
}
],
"last_message_id": "3343820033257021450",
"type": 3,
"id": "319674150115710528",
"owner_id": "82198810841029460"
}
```
###### Example Channel Category
```json theme={"system"}
{
"permission_overwrites": [],
"name": "Test",
"parent_id": null,
"nsfw": false,
"position": 0,
"guild_id": "290926798629997250",
"type": 4,
"id": "399942396007890945"
}
```
###### Example Thread Channel
[Threads](/developers/topics/threads) can be either `archived` or `active`. Archived threads are generally immutable. To send a message or add a reaction, a thread must first be unarchived. The API will helpfully automatically unarchive a thread when sending a message in that thread.
Unlike with channels, the API will only sync updates to users about threads the current user can view. When receiving a [guild create](/developers/events/gateway-events#guild-create) payload, the API will only include active threads the current user can view. Threads inside of private channels are completely private to the members of that private channel. As such, when *gaining* access to a channel the API sends a [thread list sync](/developers/events/gateway-events#thread-list-sync), which includes all active threads in that channel.
Threads also track membership. Users must be added to a thread before sending messages in them. The API will helpfully automatically add users to a thread when sending a message in that thread.
Guilds have limits on the number of active threads and members per thread. Once these are reached additional threads cannot be created or unarchived, and users cannot be added. Threads do not count against the per-guild channel limit.
The [threads](/developers/topics/threads) topic has some more information.
```json theme={"system"}
{
"id": "41771983423143937",
"guild_id": "41771983423143937",
"parent_id": "41771983423143937",
"owner_id": "41771983423143937",
"name": "don't buy dota-2",
"type": 11,
"last_message_id": "155117677105512449",
"message_count": 1,
"member_count": 5,
"rate_limit_per_user": 2,
"thread_metadata": {
"archived": false,
"auto_archive_duration": 1440,
"archive_timestamp": "2021-04-12T23:40:39.855793+00:00",
"locked": false
},
"total_message_sent": 1
}
```
### Followed Channel Object
###### Followed Channel Structure
| Field | Type | Description |
| ----------- | --------- | ------------------------- |
| channel\_id | snowflake | source channel id |
| webhook\_id | snowflake | created target webhook id |
### Overwrite Object
See [permissions](/developers/topics/permissions#permissions) for more information about the `allow` and `deny` fields.
###### Overwrite Structure
| Field | Type | Description |
| ----- | --------- | ----------------------------- |
| id | snowflake | role or user id |
| type | int | either 0 (role) or 1 (member) |
| allow | string | permission bit set |
| deny | string | permission bit set |
### Thread Metadata Object
The thread metadata object contains a number of thread-specific channel fields that are not needed by other channel types.
###### Thread Metadata Structure
| Field | Type | Description |
| ----------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
| archived | boolean | whether the thread is archived |
| auto\_archive\_duration | integer | the thread will stop showing in the channel list after `auto_archive_duration` minutes of inactivity, can be set to: 60, 1440, 4320, 10080 |
| archive\_timestamp | ISO8601 timestamp | timestamp when the thread's archive status was last changed, used for calculating recent activity |
| locked | boolean | whether the thread is locked; when a thread is locked, only users with MANAGE\_THREADS can unarchive it |
| invitable? | boolean | whether non-moderators can add other non-moderators to a thread; only available on private threads |
| create\_timestamp? | ?ISO8601 timestamp | timestamp when the thread was created; only populated for threads created after 2022-01-09 |
### Thread Member Object
A thread member object contains information about a user that has joined a thread.
###### Thread Member Structure
| Field | Type | Description |
| --------------- | ---------------------------------------------------------------------- | --------------------------------------------------------------- |
| id? \* | snowflake | ID of the thread |
| user\_id? \* | snowflake | ID of the user |
| join\_timestamp | ISO8601 timestamp | Time the user last joined the thread |
| flags | integer | Any user-thread settings, currently only used for notifications |
| member? \* \*\* | [guild member](/developers/resources/guild#guild-member-object) object | Additional information about the user |
\* These fields are omitted on the member sent within each thread in the [GUILD\_CREATE](/developers/events/gateway-events#guild-create) event.
\*\* The `member` field is only present when `with_member` is set to `true` when calling [List Thread Members](/developers/resources/channel#list-thread-members) or [Get Thread Member](/developers/resources/channel#get-thread-member).
### Default Reaction Object
An object that specifies the emoji to use as the default way to react to a forum post. Exactly one of `emoji_id` and `emoji_name` must be set.
###### Default Reaction Structure
| Field | Type | Description |
| ----------- | ---------- | ---------------------------------- |
| emoji\_id | ?snowflake | the id of a guild's custom emoji |
| emoji\_name | ?string | the unicode character of the emoji |
### Forum Tag Object
An object that represents a tag that is able to be applied to a thread in a `GUILD_FORUM` or `GUILD_MEDIA` channel.
###### Forum Tag Structure
When updating a `GUILD_FORUM` or a `GUILD_MEDIA` channel, tag objects in `available_tags` only require the `name` field.
| Field | Type | Description |
| ----------- | ---------- | -------------------------------------------------------------------------------------------------------------- |
| id | snowflake | the id of the tag |
| name | string | the name of the tag (0-20 characters) |
| moderated | boolean | whether this tag can only be added to or removed from threads by a member with the `MANAGE_THREADS` permission |
| emoji\_id | ?snowflake | the id of a guild's custom emoji \* |
| emoji\_name | ?string | the unicode character of the emoji \* |
\* At most one of `emoji_id` and `emoji_name` may be set to a non-null value.
## Get Channel
/channels/[\{channel.id}](/developers/resources/channel#channel-object)
Get a channel by ID. Returns a [channel](/developers/resources/channel#channel-object) object. If the channel is a thread, a [thread member](/developers/resources/channel#thread-member-object) object is included in the returned result.
## Modify Channel
/channels/[\{channel.id}](/developers/resources/channel#channel-object)
Update a channel's settings. Returns a [channel](/developers/resources/channel#channel-object) on success, and a 400 BAD REQUEST on invalid parameters.
All parameters to this endpoint are optional.
This endpoint supports the `X-Audit-Log-Reason` header.
###### JSON Params (Group DM)
Fires a [Channel Update](/developers/events/gateway-events#channel-update) Gateway event.
| Field | Type | Description |
| ----- | ------ | ---------------------------- |
| name | string | 1-100 character channel name |
| icon | binary | base64 encoded icon |
###### JSON Params (Guild channel)
Requires the `MANAGE_CHANNELS` permission for the guild. Fires a [Channel Update](/developers/events/gateway-events#channel-update) Gateway event. If modifying a category, individual [Channel Update](/developers/events/gateway-events#channel-update) events will fire for each child channel that also changes. If modifying permission overwrites, the `MANAGE_ROLES` permission is required. Only permissions your bot has in the guild or parent channel (if applicable) can be allowed/denied (unless your bot has a `MANAGE_ROLES` overwrite in the channel).
| Field | Type | Description | Channel Type |
| --------------------------------------- | ------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- |
| name | string | 1-100 character channel name | All |
| type | integer | the [type of channel](/developers/resources/channel#channel-object-channel-types); only conversion between text and announcement is supported and only in guilds with the "NEWS" feature | Text, Announcement |
| position | ?integer | the position of the channel in the left-hand listing (channels with the same position are sorted by id) | All |
| topic | ?string | 0-1024 character channel topic (0-4096 characters for `GUILD_FORUM` and `GUILD_MEDIA` channels) | Text, Announcement, Forum, Media |
| nsfw | ?boolean | whether the channel is [age-restricted](https://support.discord.com/hc/en-us/articles/115000084051) | Text, Voice, Announcement, Stage, Forum, Media |
| rate\_limit\_per\_user | ?integer | amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission `BYPASS_SLOWMODE`, are unaffected | Text, Voice, Stage, Forum, Media |
| bitrate\* | ?integer | the bitrate (in bits per second) of the voice or stage channel; min 8000 | Voice, Stage |
| user\_limit | ?integer | the user limit of the voice or stage channel, max 99 for voice channels and 10,000 for stage channels (0 refers to no limit) | Voice, Stage |
| permission\_overwrites\*\* | ?array of partial [overwrite](/developers/resources/channel#overwrite-object) objects | channel or category-specific permissions | All |
| parent\_id | ?snowflake | id of the new parent category for a channel | Text, Voice, Announcement, Stage, Forum, Media |
| rtc\_region | ?string | channel [voice region](/developers/resources/voice#voice-region-object) id, automatic when set to null | Voice, Stage |
| video\_quality\_mode | ?integer | the camera [video quality mode](/developers/resources/channel#channel-object-video-quality-modes) of the voice channel | Voice, Stage |
| default\_auto\_archive\_duration | ?integer | the default duration that the clients use (not the API) for newly created threads in the channel, in minutes, to automatically archive the thread after recent activity | Text, Announcement, Forum, Media |
| flags | integer | [channel flags](/developers/resources/channel#channel-object-channel-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field). Currently only `REQUIRE_TAG` (`1 << 4`) is supported by `GUILD_FORUM` and `GUILD_MEDIA` channels. `HIDE_MEDIA_DOWNLOAD_OPTIONS` (`1 << 15`) is supported only by `GUILD_MEDIA` channels | Forum, Media |
| available\_tags | array of [tag](/developers/resources/channel#forum-tag-object) objects | the set of tags that can be used in a `GUILD_FORUM` or a `GUILD_MEDIA` channel; limited to 20 | Forum, Media |
| default\_reaction\_emoji | ?[default reaction](/developers/resources/channel#default-reaction-object) object | the emoji to show in the add reaction button on a thread in a `GUILD_FORUM` or a `GUILD_MEDIA` channel | Forum, Media |
| default\_thread\_rate\_limit\_per\_user | integer | the initial `rate_limit_per_user` to set on newly created threads in a channel. this field is copied to the thread at creation time and does not live update. | Text, Forum, Media |
| default\_sort\_order | ?integer | the [default sort order type](/developers/resources/channel#channel-object-sort-order-types) used to order posts in `GUILD_FORUM` and `GUILD_MEDIA` channels | Forum, Media |
| default\_forum\_layout | integer | the [default forum layout type](/developers/resources/channel#channel-object-forum-layout-types) used to display posts in `GUILD_FORUM` channels | Forum |
\* For voice channels, normal servers can set bitrate up to 96000, servers with Boost level 1 can set up to 128000, servers with Boost level 2 can set up to 256000, and servers with Boost level 3 or the `VIP_REGIONS` [guild feature](/developers/resources/guild#guild-object-guild-features) can set up to 384000. For stage channels, bitrate can be set up to 64000.
\*\* In each overwrite object, the `allow` and `deny` keys can be omitted or set to `null`, which both default to `"0"`.
###### JSON Params (Thread)
When setting `archived` to `false`, when `locked` is also `false`, only the `SEND_MESSAGES` permission is required.
Otherwise, requires the `MANAGE_THREADS` permission. Fires a [Thread Update](/developers/events/gateway-events#thread-update) Gateway event. Requires the thread to have `archived` set to `false` or be set to `false` in the request.
| Field | Type | Description |
| ----------------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name | string | 1-100 character channel name |
| archived | boolean | whether the thread is archived |
| auto\_archive\_duration | integer | the thread will stop showing in the channel list after `auto_archive_duration` minutes of inactivity, can be set to: 60, 1440, 4320, 10080 |
| locked | boolean | whether the thread is locked; when a thread is locked, only users with MANAGE\_THREADS can unarchive it |
| invitable | boolean | whether non-moderators can add other non-moderators to a thread; only available on private threads |
| rate\_limit\_per\_user | ?integer | amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission `BYPASS_SLOWMODE`, are unaffected |
| flags? | integer | [channel flags](/developers/resources/channel#channel-object-channel-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field); `PINNED` can only be set for threads in forum and media channels |
| applied\_tags? | array of snowflakes | the IDs of the set of tags that have been applied to a thread in a `GUILD_FORUM` or a `GUILD_MEDIA` channel; limited to 5 |
## Set Voice Channel Status
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/voice-status
Set a voice channel's status. Requires the `SET_VOICE_CHANNEL_STATUS` permission, and additionally the `MANAGE_CHANNELS` permission if the current user is not connected to the voice channel. Fires a [Voice Channel Status Update](/developers/events/gateway-events#voice-channel-status-update) Gateway event.
This endpoint supports the `X-Audit-Log-Reason` header.
###### JSON Params
| Field | Type | Description |
| ------ | ------- | ----------------------------------------------- |
| status | ?string | new voice channel status (up to 500 characters) |
## Delete/Close Channel
/channels/[\{channel.id}](/developers/resources/channel#channel-object)
Delete a channel, or close a private message. Requires the `MANAGE_CHANNELS` permission for the guild, or `MANAGE_THREADS` if the channel is a thread. Deleting a category does not delete its child channels; they will have their `parent_id` removed and a [Channel Update](/developers/events/gateway-events#channel-update) Gateway event will fire for each of them. Returns a [channel](/developers/resources/channel#channel-object) object on success. Fires a [Channel Delete](/developers/events/gateway-events#channel-delete) Gateway event (or [Thread Delete](/developers/events/gateway-events#thread-delete) if the channel was a thread).
Deleting a guild channel cannot be undone. Use this with caution, as it is impossible to undo this action when performed on a guild channel. In contrast, when used with a private message, it is possible to undo the action by opening a private message with the recipient again.
For Community guilds, the Rules or Guidelines channel and the Community Updates channel cannot be deleted.
This endpoint supports the `X-Audit-Log-Reason` header.
## Edit Channel Permissions
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/permissions/[\{overwrite.id}](/developers/resources/channel#overwrite-object)
Edit the channel permission overwrites for a user or role in a channel. Only usable for guild channels. Requires the `MANAGE_ROLES` permission. Only permissions your bot has in the guild or parent channel (if applicable) can be allowed/denied (unless your bot has a `MANAGE_ROLES` overwrite in the channel). Returns a 204 empty response on success. Fires a [Channel Update](/developers/events/gateway-events#channel-update) Gateway event. For more information about permissions, see [permissions](/developers/topics/permissions#permissions).
This endpoint supports the `X-Audit-Log-Reason` header.
###### JSON Params
| Field | Type | Description |
| ------ | ------- | --------------------------------------------------------------- |
| allow? | string? | the bitwise value of all allowed permissions (default `"0"`) |
| deny? | string? | the bitwise value of all disallowed permissions (default `"0"`) |
| type | integer | 0 for a role or 1 for a member |
## Get Channel Invites
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/invites
Returns a list of [invite](/developers/resources/invite#invite-object) objects (with [invite metadata](/developers/resources/invite#invite-metadata-object)) for the channel. Only usable for guild channels. Requires the `MANAGE_CHANNELS` permission.
## Create Channel Invite
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/invites
Create a new [invite](/developers/resources/invite#invite-object) object for the channel. Only usable for guild channels. Requires the `CREATE_INSTANT_INVITE` permission. All JSON parameters for this route are optional, however the request body is not. If you are not sending any fields, you still have to send an empty JSON object (`{}`). Returns an [invite](/developers/resources/invite#invite-object) object. Fires an [Invite Create](/developers/events/gateway-events#invite-create) Gateway event.
This endpoint supports the `X-Audit-Log-Reason` header.
###### JSON/Form Params
| Field | Type | Description | Default |
| ----------------------- | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ---------------- |
| max\_age | integer | duration of invite in seconds before expiry, or 0 for never. between 0 and 604800 (7 days) | 86400 (24 hours) |
| max\_uses | integer | max number of uses or 0 for unlimited. between 0 and 100 | 0 |
| temporary | boolean | whether this invite only grants temporary membership | false |
| unique | boolean | if true, don't try to reuse a similar invite (useful for creating many unique one time use invites) | false |
| target\_type | integer | the [type of target](/developers/resources/invite#invite-object-invite-target-types) for this voice channel invite | |
| target\_user\_id | snowflake | the id of the user whose stream to display for this invite, required if `target_type` is 1, the user must be streaming in the channel | |
| target\_application\_id | snowflake | the id of the embedded application to open for this invite, required if `target_type` is 2, the application must have the `EMBEDDED` flag | |
| target\_users\_file?\* | file | a csv file with a single column of user IDs for all the users able to accept this invite | |
| payload\_json? | string | JSON-encoded body of non-file params, only for `multipart/form-data` requests. | |
| role\_ids?\*\* | array of snowflakes | the role ID(s) for [roles](/developers/topics/permissions#role-object) in the guild given to the users that accept this invite | |
\* Requires `multipart/form-data` as the content type with other parameters as form fields in the multipart body. Uploading a file with invalid user IDs will result in a 400 with the invalid IDs described. Duplicate user IDs in the file will be ignored.
\*\* Requires the `MANAGE_ROLES` permission and cannot assign roles with higher permissions than the sender.
## Delete Channel Permission
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/permissions/[\{overwrite.id}](/developers/resources/channel#overwrite-object)
Delete a channel permission overwrite for a user or role in a channel. Only usable for guild channels. Requires the `MANAGE_ROLES` permission. Returns a 204 empty response on success. Fires a [Channel Update](/developers/events/gateway-events#channel-update) Gateway event. For more information about permissions, see [permissions](/developers/topics/permissions#permissions)
This endpoint supports the `X-Audit-Log-Reason` header.
## Follow Announcement Channel
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/followers
Follow an Announcement Channel to send messages to a target channel. Requires the `MANAGE_WEBHOOKS` permission in the target channel. Returns a [followed channel](/developers/resources/channel#followed-channel-object) object. Fires a [Webhooks Update](/developers/events/gateway-events#webhooks-update) Gateway event for the target channel.
This endpoint supports the `X-Audit-Log-Reason` header.
###### JSON Params
| Field | Type | Description |
| -------------------- | --------- | -------------------- |
| webhook\_channel\_id | snowflake | id of target channel |
## Trigger Typing Indicator
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/typing
Post a typing indicator for the specified channel, which expires after 10 seconds. Returns a 204 empty response on success. Fires a [Typing Start](/developers/events/gateway-events#typing-start) Gateway event.
Generally bots should **not** use this route. However, if a bot is responding to a command and expects the computation to take a few seconds, this endpoint may be called to let the user know that the bot is processing their message.
## Group DM Add Recipient
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/recipients/[\{user.id}](/developers/resources/user#user-object)
Adds a recipient to a Group DM using their access token.
###### JSON Params
| Field | Type | Description |
| ------------- | ------ | --------------------------------------------------------------------- |
| access\_token | string | access token of a user that has granted your app the `gdm.join` scope |
| nick | string | nickname of the user being added |
## Group DM Remove Recipient
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/recipients/[\{user.id}](/developers/resources/user#user-object)
Removes a recipient from a Group DM.
## Start Thread from Message
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/messages/[\{message.id}](/developers/resources/message#message-object)/threads
Creates a new thread from an existing message. Returns a [channel](/developers/resources/channel#channel-object) on success, and a 400 BAD REQUEST on invalid parameters. Fires a [Thread Create](/developers/events/gateway-events#thread-create) and a [Message Update](/developers/events/gateway-events#message-update) Gateway event.
When called on a `GUILD_TEXT` channel, creates a `PUBLIC_THREAD`. When called on a `GUILD_ANNOUNCEMENT` channel, creates a `ANNOUNCEMENT_THREAD`. Does not work on a [`GUILD_FORUM`](/developers/resources/channel#start-thread-in-forum-or-media-channel) or a `GUILD_MEDIA` channel. The id of the created thread will be the same as the id of the source message, and as such a message can only have a single thread created from it.
This endpoint supports the `X-Audit-Log-Reason` header.
###### JSON Params
| Field | Type | Description |
| ------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| name | string | 1-100 character channel name |
| auto\_archive\_duration? | integer | the thread will stop showing in the channel list after `auto_archive_duration` minutes of inactivity, can be set to: 60, 1440, 4320, 10080 |
| rate\_limit\_per\_user? | ?integer | amount of seconds a user has to wait before sending another message (0-21600) |
## Start Thread without Message
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/threads
Creates a new thread that is not connected to an existing message. Returns a [channel](/developers/resources/channel#channel-object) on success, and a 400 BAD REQUEST on invalid parameters. Fires a [Thread Create](/developers/events/gateway-events#thread-create) Gateway event.
This endpoint supports the `X-Audit-Log-Reason` header.
###### JSON Params
| Field | Type | Description |
| ------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| name | string | 1-100 character channel name |
| auto\_archive\_duration? | integer | the thread will stop showing in the channel list after `auto_archive_duration` minutes of inactivity, can be set to: 60, 1440, 4320, 10080 |
| type?\* | integer | the [type of thread](/developers/resources/channel#channel-object-channel-types) to create |
| invitable? | boolean | whether non-moderators can add other non-moderators to a thread; only available when creating a private thread |
| rate\_limit\_per\_user? | ?integer | amount of seconds a user has to wait before sending another message (0-21600) |
\* `type` currently defaults to `PRIVATE_THREAD` in order to match the behavior when thread documentation was first published. In a future API version this will be changed to be a required field, with no default.
## Start Thread in Forum or Media Channel
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/threads
Creates a new thread in a forum or a media channel, and sends a message within the created thread. Returns a [channel](/developers/resources/channel#channel-object), with a nested [message](/developers/resources/message#message-object) object, on success, and a 400 BAD REQUEST on invalid parameters. Fires a [Thread Create](/developers/events/gateway-events#thread-create) and [Message Create](/developers/events/gateway-events#message-create) Gateway event.
* The type of the created thread is `PUBLIC_THREAD`.
* See [message formatting](/developers/reference#message-formatting) for more information on how to properly format messages.
* The current user must have the `SEND_MESSAGES` permission (`CREATE_PUBLIC_THREADS` is ignored).
* The maximum request size when sending a message is **25 MiB**.
* For the embed object, you can set every field except `type` (it will be `rich` regardless of if you try to set it), `provider`, `video`, and any `height`, `width`, or `proxy_url` values for images.
* Examples for file uploads are available in [Uploading Files](/developers/reference#uploading-files).
* Files must be attached using a `multipart/form-data` body as described in [Uploading Files](/developers/reference#uploading-files).
* Note that when sending a message, you must provide a value for at **least one of** `content`, `embeds`, `sticker_ids`, `components`, or `files[n]`.
Discord may strip certain characters from message content, like invalid unicode characters or characters which cause unexpected message formatting. If you are passing user-generated strings into message content, consider sanitizing the data to prevent unexpected behavior and using `allowed_mentions` to prevent unexpected mentions.
This endpoint supports the `X-Audit-Log-Reason` header.
###### JSON/Form Params
| Field | Type | Description |
| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| name | string | 1-100 character channel name |
| auto\_archive\_duration?\* | integer | duration in minutes to automatically archive the thread after recent activity, can be set to: 60, 1440, 4320, 10080 |
| rate\_limit\_per\_user? | ?integer | amount of seconds a user has to wait before sending another message (0-21600) |
| message | a [forum thread message params](/developers/resources/channel#start-thread-in-forum-or-media-channel-forum-and-media-thread-message-params-object) object | contents of the first message in the forum/media thread |
| applied\_tags? | array of snowflakes | the IDs of the set of tags that have been applied to a thread in a `GUILD_FORUM` or a `GUILD_MEDIA` channel |
| files\[n]?\* | file contents | Contents of the file being sent. See [Uploading Files](/developers/reference#uploading-files) |
| payload\_json? | string | JSON-encoded body of non-file params, only for `multipart/form-data` requests. See [Uploading Files](/developers/reference#uploading-files) |
###### Forum and Media Thread Message Params Object
When sending a message, apps must provide a value for **at least one of** `content`, `embeds`, `sticker_ids`, `components`, or `files[n]`.
| Field | Type | Description |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| content?\* | string | Message contents (up to 2000 characters) |
| embeds?\* | array of [embed](/developers/resources/message#embed-object) objects | Up to 10 `rich` embeds (up to 6000 characters) |
| allowed\_mentions? | [allowed mention object](/developers/resources/message#allowed-mentions-object) | Allowed mentions for the message |
| components?\* | array of [message component](/developers/components/reference#component-object) objects | Components to include with the message |
| sticker\_ids?\* | array of snowflakes | IDs of up to 3 [stickers](/developers/resources/sticker#sticker-object) in the server to send in the message |
| attachments? | array of partial [attachment request](/developers/resources/message#attachment-object-attachment-request-structure) objects | Metadata for the attachments. See [Uploading Files](/developers/reference#uploading-files) |
| flags? | integer | [Message flags](/developers/resources/message#message-object-message-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) (only `SUPPRESS_EMBEDS` and `SUPPRESS_NOTIFICATIONS` can be set) |
\* At least one of `content`, `embeds`, `sticker_ids`, `components`, or `files[n]` is required.
## Join Thread
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/thread-members/@me
Adds the current user to a thread. Also requires the thread is not archived. Returns a 204 empty response on success. Fires a [Thread Members Update](/developers/events/gateway-events#thread-members-update) and a [Thread Create](/developers/events/gateway-events#thread-create) Gateway event.
## Add Thread Member
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/thread-members/[\{user.id}](/developers/resources/user#user-object)
Adds another member to a thread. Requires the ability to send messages in the thread. Also requires the thread is not archived. Returns a 204 empty response if the member is successfully added or was already a member of the thread. Fires a [Thread Members Update](/developers/events/gateway-events#thread-members-update) Gateway event.
## Leave Thread
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/thread-members/@me
Removes the current user from a thread. Also requires the thread is not archived. Returns a 204 empty response on success. Fires a [Thread Members Update](/developers/events/gateway-events#thread-members-update) Gateway event.
## Remove Thread Member
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/thread-members/[\{user.id}](/developers/resources/user#user-object)
Removes another member from a thread. Requires the `MANAGE_THREADS` permission, or the creator of the thread if it is a `PRIVATE_THREAD`. Also requires the thread is not archived. Returns a 204 empty response on success. Fires a [Thread Members Update](/developers/events/gateway-events#thread-members-update) Gateway event.
## Get Thread Member
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/thread-members/[\{user.id}](/developers/resources/user#user-object)
Returns a [thread member](/developers/resources/channel#thread-member-object) object for the specified user if they are a member of the thread, returns a 404 response otherwise.
When `with_member` is set to `true`, the thread member object will include a `member` field containing a [guild member](/developers/resources/guild#guild-member-object) object.
###### Query String Params
| Field | Type | Description |
| ------------- | ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------- |
| with\_member? | [boolean](/developers/reference#boolean-query-strings) | Whether to include a [guild member](/developers/resources/guild#guild-member-object) object for the thread member |
## List Thread Members
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/thread-members
Starting in API v11, this endpoint will always return paginated results. Paginated results can be enabled before API v11 by setting `with_member` to `true`. Read [the changelog](/developers/change-log#thread-member-details-and-pagination) for details.
Returns array of [thread members](/developers/resources/channel#thread-member-object) objects that are members of the thread.
When `with_member` is set to `true`, the results will be paginated and each thread member object will include a `member` field containing a [guild member](/developers/resources/guild#guild-member-object) object.
This endpoint is restricted according to whether the `GUILD_MEMBERS` [Privileged Intent](/developers/events/gateway#privileged-intents) is enabled for your application.
###### Query String Params
| Field | Type | Description |
| ------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------ |
| with\_member? | [boolean](/developers/reference#boolean-query-strings) | Whether to include a [guild member](/developers/resources/guild#guild-member-object) object for each thread member |
| after? | snowflake | Get thread members after this user ID |
| limit? | integer | Max number of thread members to return (1-100). Defaults to 100. |
## List Public Archived Threads
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/threads/archived/public
Returns archived threads in the channel that are public. When called on a `GUILD_TEXT` channel, returns threads of [type](/developers/resources/channel#channel-object-channel-types) `PUBLIC_THREAD`. When called on a `GUILD_ANNOUNCEMENT` channel returns threads of [type](/developers/resources/channel#channel-object-channel-types) `ANNOUNCEMENT_THREAD`. Threads are ordered by `archive_timestamp`, in descending order. Requires the `READ_MESSAGE_HISTORY` permission.
###### Query String Params
| Field | Type | Description |
| ------- | ----------------- | ---------------------------------------------- |
| before? | ISO8601 timestamp | returns threads archived before this timestamp |
| limit? | integer | optional maximum number of threads to return |
###### Response Body
| Field | Type | Description |
| --------- | ------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| threads | array of [channel](/developers/resources/channel#channel-object) objects | the public, archived threads |
| members | array of [thread members](/developers/resources/channel#thread-member-object) objects | a thread member object for each returned thread the current user has joined |
| has\_more | boolean | whether there are potentially additional threads that could be returned on a subsequent call |
## List Private Archived Threads
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/threads/archived/private
Returns archived threads in the channel that are of [type](/developers/resources/channel#channel-object-channel-types) `PRIVATE_THREAD`. Threads are ordered by `archive_timestamp`, in descending order. Requires both the `READ_MESSAGE_HISTORY` and `MANAGE_THREADS` permissions.
###### Query String Params
| Field | Type | Description |
| ------- | ----------------- | ---------------------------------------------- |
| before? | ISO8601 timestamp | returns threads archived before this timestamp |
| limit? | integer | optional maximum number of threads to return |
###### Response Body
| Field | Type | Description |
| --------- | ------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| threads | array of [channel](/developers/resources/channel#channel-object) objects | the private, archived threads |
| members | array of [thread members](/developers/resources/channel#thread-member-object) objects | a thread member object for each returned thread the current user has joined |
| has\_more | boolean | whether there are potentially additional threads that could be returned on a subsequent call |
## List Joined Private Archived Threads
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/users/@me/threads/archived/private
Returns archived threads in the channel that are of [type](/developers/resources/channel#channel-object-channel-types) `PRIVATE_THREAD`, and the user has joined. Threads are ordered by their `id`, in descending order. Requires the `READ_MESSAGE_HISTORY` permission.
###### Query String Params
| Field | Type | Description |
| ------- | --------- | -------------------------------------------- |
| before? | snowflake | returns threads before this id |
| limit? | integer | optional maximum number of threads to return |
###### Response Body
| Field | Type | Description |
| --------- | ------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| threads | array of [channel](/developers/resources/channel#channel-object) objects | the private, archived threads the current user has joined |
| members | array of [thread members](/developers/resources/channel#thread-member-object) objects | a thread member object for each returned thread the current user has joined |
| has\_more | boolean | whether there are potentially additional threads that could be returned on a subsequent call |
# Emoji Resource
Source: https://docs.discord.com/developers/resources/emoji
Reference for Discord emoji objects and management endpoints.
Routes for controlling emojis do not follow the normal rate limit conventions. These routes are specifically limited on a per-guild basis to prevent abuse. This means that the quota returned by our APIs may be inaccurate, and you may encounter 429s.
### Emoji Object
###### Emoji Structure
| Field | Type | Description |
| ---------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| id | ?snowflake | [emoji id](/developers/reference#image-formatting) |
| name | ?string (can be null only in reaction emoji objects) | emoji name |
| roles? | array of [role](/developers/topics/permissions#role-object) object ids | roles allowed to use this emoji |
| user? | [user](/developers/resources/user#user-object) object | user that created this emoji |
| require\_colons? | boolean | whether this emoji must be wrapped in colons |
| managed? | boolean | whether this emoji is managed |
| animated? | boolean | whether this emoji is animated |
| available? | boolean | whether this emoji can be used, may be false due to loss of Server Boosts |
###### Premium Emoji
Roles with the `integration_id` tag being the guild's guild\_subscription integration are considered subscription roles.
An emoji cannot have both subscription roles and non-subscription roles.
Emojis with subscription roles are considered premium emoji, and count toward a separate limit of 25.
Emojis cannot be converted between normal and premium after creation.
###### Emoji Formats
Emoji can be uploaded as JPEG, PNG, GIF, WebP, and AVIF formats. All emoji (regardless of original format) can be served as WebP. We highly recommend that developers use the `.webp` extension when fetching emoji so they're rendered as WebP for maximum performance and compatibility. The Discord client uses WebP for all emoji displayed in-app.
Still WebP emoji can be requested using the `.webp` file extension. For animated WebP emoji, use the `.webp` extension with the `?animated=true` query parameter.
###### Application-Owned Emoji
An application can own up to 2000 emojis that can only be used by that app.
App emojis can be managed using the API with a bot token, or using the app's settings in the portal.
The `USE_EXTERNAL_EMOJIS` permission is not required to use app emojis.
The `user` field of an app emoji object represents the team member that uploaded the emoji from the app's settings, or the bot user if uploaded using the API.
###### Emoji Example
```json theme={"system"}
{
"id": "41771983429993937",
"name": "LUL",
"roles": ["41771983429993000", "41771983429993111"],
"user": {
"username": "Luigi",
"discriminator": "0002",
"id": "96008815106887111",
"avatar": "5500909a3274e1812beb4e8de6631111",
"public_flags": 131328
},
"require_colons": true,
"managed": false,
"animated": false
}
```
###### Standard Emoji Example
```json theme={"system"}
{
"id": null,
"name": "🔥"
}
```
###### Custom Emoji Examples
In `MESSAGE_REACTION_ADD`, `MESSAGE_REACTION_REMOVE` and `MESSAGE_REACTION_REMOVE_EMOJI` gateway events `animated` will be returned for animated emoji.
In `MESSAGE_REACTION_ADD` and `MESSAGE_REACTION_REMOVE` gateway events `name` may be `null` when custom emoji data is not available (for example, if it was deleted from the guild).
```json theme={"system"}
{
"id": "41771983429993937",
"name": "LUL",
"animated": true
}
```
```json theme={"system"}
{
"id": "41771983429993937",
"name": null
}
```
## List Guild Emojis
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/emojis
Returns a list of [emoji](/developers/resources/emoji#emoji-object) objects for the given guild. Includes `user` fields if the bot has the `CREATE_GUILD_EXPRESSIONS` or `MANAGE_GUILD_EXPRESSIONS` permission.
## Get Guild Emoji
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/emojis/[\{emoji.id}](/developers/resources/emoji#emoji-object)
Returns an [emoji](/developers/resources/emoji#emoji-object) object for the given guild and emoji IDs. Includes the `user` field if the bot has the `MANAGE_GUILD_EXPRESSIONS` permission, or if the bot created the emoji and has the `CREATE_GUILD_EXPRESSIONS` permission.
## Create Guild Emoji
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/emojis
Create a new emoji for the guild. Requires the `CREATE_GUILD_EXPRESSIONS` permission. Returns the new [emoji](/developers/resources/emoji#emoji-object) object on success. Fires a [Guild Emojis Update](/developers/events/gateway-events#guild-emojis-update) Gateway event.
Emojis and animated emojis have a maximum file size of 256 KiB. Attempting to upload an emoji larger than this limit will fail and return 400 Bad Request and an error message, but not a [JSON status code](/developers/topics/opcodes-and-status-codes#json).
We highly recommend that developers use the `.webp` extension when fetching emoji so they're rendered as WebP for maximum performance and compatibility. See the [Emoji Formats](/developers/resources/emoji#emoji-object-emoji-formats) section above for more details.
This endpoint supports the `X-Audit-Log-Reason` header.
###### JSON Params
| Field | Type | Description |
| ----- | ---------------------------------------------- | ------------------------------- |
| name | string | name of the emoji |
| image | [image data](/developers/reference#image-data) | the 128x128 emoji image |
| roles | array of snowflakes | roles allowed to use this emoji |
## Modify Guild Emoji
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/emojis/[\{emoji.id}](/developers/resources/emoji#emoji-object)
Modify the given emoji. For emojis created by the current user, requires either the `CREATE_GUILD_EXPRESSIONS` or `MANAGE_GUILD_EXPRESSIONS` permission. For other emojis, requires the `MANAGE_GUILD_EXPRESSIONS` permission. Returns the updated [emoji](/developers/resources/emoji#emoji-object) object on success. Fires a [Guild Emojis Update](/developers/events/gateway-events#guild-emojis-update) Gateway event.
All parameters to this endpoint are optional.
This endpoint supports the `X-Audit-Log-Reason` header.
###### JSON Params
| Field | Type | Description |
| ----- | -------------------- | ------------------------------- |
| name | string | name of the emoji |
| roles | ?array of snowflakes | roles allowed to use this emoji |
## Delete Guild Emoji
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/emojis/[\{emoji.id}](/developers/resources/emoji#emoji-object)
Delete the given emoji. For emojis created by the current user, requires either the `CREATE_GUILD_EXPRESSIONS` or `MANAGE_GUILD_EXPRESSIONS` permission. For other emojis, requires the `MANAGE_GUILD_EXPRESSIONS` permission. Returns `204 No Content` on success. Fires a [Guild Emojis Update](/developers/events/gateway-events#guild-emojis-update) Gateway event.
This endpoint supports the `X-Audit-Log-Reason` header.
## List Application Emojis
/applications/[\{application.id}](/developers/resources/application#application-object)/emojis
Returns an object containing a list of [emoji](/developers/resources/emoji#emoji-object) objects for the given application under the `items` key. Includes a `user` object for the team member that uploaded the emoji from the app's settings, or for the bot user if uploaded using the API.
```json theme={"system"}
{
"items": [
{
"id": "41771983429993937",
"name": "LUL",
"roles": [],
"user": {
"username": "Luigi",
"discriminator": "0002",
"id": "96008815106887111",
"avatar": "5500909a3274e1812beb4e8de6631111",
"public_flags": 131328
},
"require_colons": true,
"managed": false,
"animated": false
}
]
}
```
## Get Application Emoji
/applications/[\{application.id}](/developers/resources/application#application-object)/emojis/[\{emoji.id}](/developers/resources/emoji#emoji-object)
Returns an [emoji](/developers/resources/emoji#emoji-object) object for the given application and emoji IDs. Includes the `user` field.
## Create Application Emoji
/applications/[\{application.id}](/developers/resources/application#application-object)/emojis
Create a new emoji for the application. Returns the new [emoji](/developers/resources/emoji#emoji-object) object on success.
Emojis and animated emojis have a maximum file size of 256 KiB. Attempting to upload an emoji larger than this limit will fail and return 400 Bad Request and an error message, but not a [JSON status code](/developers/topics/opcodes-and-status-codes#json).
We highly recommend that developers use the `.webp` extension when fetching emoji so they're rendered as WebP for maximum performance and compatibility. See the [Emoji Formats](/developers/resources/emoji#emoji-object-emoji-formats) section above for more details.
###### JSON Params
| Field | Type | Description |
| ----- | ---------------------------------------------- | ----------------------- |
| name | string | name of the emoji |
| image | [image data](/developers/reference#image-data) | the 128x128 emoji image |
## Modify Application Emoji
/applications/[\{application.id}](/developers/resources/application#application-object)/emojis/[\{emoji.id}](/developers/resources/emoji#emoji-object)
Modify the given emoji. Returns the updated [emoji](/developers/resources/emoji#emoji-object) object on success.
###### JSON Params
| Field | Type | Description |
| ----- | ------ | ----------------- |
| name | string | name of the emoji |
## Delete Application Emoji
/applications/[\{application.id}](/developers/resources/application#application-object)/emojis/[\{emoji.id}](/developers/resources/emoji#emoji-object)
Delete the given emoji. Returns `204 No Content` on success.
# Entitlement Resource
Source: https://docs.discord.com/developers/resources/entitlement
Reference for Discord entitlement objects that represent user access to premium features.
Entitlements in Discord represent that a user or guild has access to a premium offering in your application.
Refer to the [Monetization Overview](/developers/monetization/overview) for more information on how to use Entitlements in your app.
### Entitlement Object
###### Entitlement Structure
| Field | Type | Description |
| --------------- | ------------------ | --------------------------------------------------------------------------------------------- |
| id | snowflake | ID of the entitlement |
| sku\_id | snowflake | ID of the SKU |
| application\_id | snowflake | ID of the parent application |
| user\_id? | snowflake | ID of the user that is granted access to the entitlement's sku |
| type | integer | [Type of entitlement](/developers/resources/entitlement#entitlement-object-entitlement-types) |
| deleted | boolean | Entitlement was deleted |
| starts\_at | ?ISO8601 timestamp | Start date at which the entitlement is valid. |
| ends\_at | ?ISO8601 timestamp | Date at which the entitlement is no longer valid. |
| guild\_id? | snowflake | ID of the guild that is granted access to the entitlement's sku |
| consumed? | boolean | For consumable items, whether or not the entitlement has been consumed |
###### Entitlement Example
```json theme={"system"}
{
"id": "1019653849998299136",
"sku_id": "1019475255913222144",
"application_id": "1019370614521200640",
"user_id": "771129655544643584",
"promotion_id": null,
"type": 8,
"deleted": false,
"gift_code_flags": 0,
"consumed": false,
"starts_at": "2022-09-14T17:00:18.704163+00:00",
"ends_at": "2022-10-14T17:00:18.704163+00:00",
"guild_id": "1015034326372454400",
"subscription_id": "1019653835926409216"
}
```
###### Entitlement Types
| Type | Value | Description |
| ------------------------- | ----- | -------------------------------------------------------------- |
| PURCHASE | 1 | Entitlement was purchased by user |
| PREMIUM\_SUBSCRIPTION | 2 | Entitlement for Discord Nitro subscription |
| DEVELOPER\_GIFT | 3 | Entitlement was gifted by developer |
| TEST\_MODE\_PURCHASE | 4 | Entitlement was purchased by a dev in application test mode |
| FREE\_PURCHASE | 5 | Entitlement was granted when the SKU was free |
| USER\_GIFT | 6 | Entitlement was gifted by another user |
| PREMIUM\_PURCHASE | 7 | Entitlement was claimed by user for free as a Nitro Subscriber |
| APPLICATION\_SUBSCRIPTION | 8 | Entitlement was purchased as an app subscription |
## List Entitlements
/applications/[\{application.id}](/developers/resources/application#application-object)/entitlements
Returns all entitlements for a given app, active and expired.
###### Query String Params
| param | type | description |
| ----------------- | ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- |
| user\_id? | snowflake | User ID to look up entitlements for |
| sku\_ids? | comma-delimited set of snowflakes | Optional list of SKU IDs to check entitlements for |
| before? | snowflake | Retrieve entitlements before this entitlement ID |
| after? | snowflake | Retrieve entitlements after this entitlement ID |
| limit? | integer | Number of entitlements to return, 1-100, default 100 |
| guild\_id? | snowflake | Guild ID to look up entitlements for |
| exclude\_ended? | [boolean](/developers/reference#boolean-query-strings) | Whether or not ended entitlements should be omitted. Defaults to false, ended entitlements are included by default. |
| exclude\_deleted? | [boolean](/developers/reference#boolean-query-strings) | Whether or not deleted entitlements should be omitted. Defaults to true, deleted entitlements are not included by default. |
```json theme={"system"}
[
{
"id": "1019653849998299136",
"sku_id": "1019475255913222144",
"application_id": "1019370614521200640",
"user_id": "771129655544643584",
"promotion_id": null,
"type": 8,
"deleted": false,
"gift_code_flags": 0,
"consumed": false,
"starts_at": "2022-09-14T17:00:18.704163+00:00",
"ends_at": "2022-10-14T17:00:18.704163+00:00",
"guild_id": "1015034326372454400",
"subscription_id": "1019653835926409216"
}
]
```
## Get Entitlement
/applications/[\{application.id}](/developers/resources/application#application-object)/entitlements/[\{entitlement.id}](/developers/resources/entitlement#entitlement-object)
Returns an entitlement.
```json theme={"system"}
{
"id": "1019653849998299136",
"sku_id": "1019475255913222144",
"application_id": "1019370614521200640",
"user_id": "771129655544643584",
"promotion_id": null,
"type": 8,
"deleted": false,
"gift_code_flags": 0,
"consumed": false,
"starts_at": "2022-09-14T17:00:18.704163+00:00",
"ends_at": "2022-10-14T17:00:18.704163+00:00",
"guild_id": "1015034326372454400",
"subscription_id": "1019653835926409216"
}
```
## Consume an Entitlement
/applications/[\{application.id}](/developers/resources/application#application-object)/entitlements/[\{entitlement.id}](/developers/resources/entitlement#entitlement-object)/consume
For One-Time Purchase consumable SKUs, marks a given entitlement for the user as consumed. The entitlement will have `consumed: true` when using [List Entitlements](/developers/resources/entitlement#list-entitlements).
Returns a `204 No Content` on success.
## Create Test Entitlement
/applications/[\{application.id}](/developers/resources/application#application-object)/entitlements
Creates a test entitlement to a given SKU for a given guild or user. Discord will act as though that user or guild has entitlement to your premium offering.
This endpoint returns a partial entitlement object. It will **not** contain `subscription_id`, `starts_at`, or `ends_at`, as it's valid in perpetuity.
After creating a test entitlement, you'll need to reload your Discord client. After doing so, you'll see that your server or user now has premium access.
###### JSON Params
| param | type | description |
| ----------- | ------- | --------------------------------------------------------- |
| sku\_id | string | ID of the SKU to grant the entitlement to |
| owner\_id | string | ID of the guild or user to grant the entitlement to |
| owner\_type | integer | `1` for a guild subscription, `2` for a user subscription |
```json theme={"system"}
{
"sku_id": "999184799365857331",
"owner_id": "847184799365857999",
"owner_type": 1
}
```
## Delete Test Entitlement
/applications/[\{application.id}](/developers/resources/application#application-object)/entitlements/[\{entitlement.id}](/developers/resources/entitlement#entitlement-object)
Deletes a currently-active test entitlement. Discord will act as though that user or guild *no longer has* entitlement to your premium offering.
Returns `204 No Content` on success.
# Guild Resource
Source: https://docs.discord.com/developers/resources/guild
Guilds in Discord represent an isolated collection of users and channels, and are often referred to as "servers" in the UI.
### Guild Object
###### Guild Structure
Fields specific to the `GUILD_CREATE` event are listed in the [Gateway Events documentation](/developers/events/gateway-events#guild-create).
| Field | Type | Description |
| ---------------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id | snowflake | guild id |
| name | string | guild name (2-100 characters, excluding trailing and leading whitespace) |
| icon | ?string | [icon hash](/developers/reference#image-formatting) |
| icon\_hash? | ?string | [icon hash](/developers/reference#image-formatting), returned when in the template object |
| splash | ?string | [splash hash](/developers/reference#image-formatting) |
| discovery\_splash | ?string | [discovery splash hash](/developers/reference#image-formatting); only present for guilds with the "DISCOVERABLE" feature |
| owner? \* | boolean | true if [the user](/developers/resources/user#get-current-user-guilds) is the owner of the guild |
| owner\_id | snowflake | id of owner |
| permissions? \* | string | total permissions for [the user](/developers/resources/user#get-current-user-guilds) in the guild (excludes overwrites and [implicit permissions](/developers/topics/permissions#implicit-permissions)) |
| region? \*\* | ?string | [voice region](/developers/resources/voice#voice-region-object) id for the guild (deprecated) |
| afk\_channel\_id | ?snowflake | id of afk channel |
| afk\_timeout | integer | afk timeout in seconds |
| widget\_enabled? | boolean | true if the server widget is enabled |
| widget\_channel\_id? | ?snowflake | the channel id that the widget will generate an invite to, or `null` if set to no invite |
| verification\_level | integer | [verification level](/developers/resources/guild#guild-object-verification-level) required for the guild |
| default\_message\_notifications | integer | default [message notifications level](/developers/resources/guild#guild-object-default-message-notification-level) |
| explicit\_content\_filter | integer | [explicit content filter level](/developers/resources/guild#guild-object-explicit-content-filter-level) |
| roles | array of [role](/developers/topics/permissions#role-object) objects | roles in the guild |
| emojis | array of [emoji](/developers/resources/emoji#emoji-object) objects | custom guild emojis |
| features | array of [guild feature](/developers/resources/guild#guild-object-guild-features) strings | enabled guild features |
| mfa\_level | integer | required [MFA level](/developers/resources/guild#guild-object-mfa-level) for the guild |
| application\_id | ?snowflake | application id of the guild creator if it is bot-created |
| system\_channel\_id | ?snowflake | the id of the channel where guild notices such as welcome messages and boost events are posted |
| system\_channel\_flags | integer | [system channel flags](/developers/resources/guild#guild-object-system-channel-flags) |
| rules\_channel\_id | ?snowflake | the id of the channel where Community guilds can display rules and/or guidelines |
| max\_presences? | ?integer | the maximum number of presences for the guild (`null` is always returned, apart from the largest of guilds) |
| max\_members? | integer | the maximum number of members for the guild |
| vanity\_url\_code | ?string | the vanity url code for the guild |
| description | ?string | the description of a guild |
| banner | ?string | [banner hash](/developers/reference#image-formatting) |
| premium\_tier | integer | [premium tier](/developers/resources/guild#guild-object-premium-tier) (Server Boost level) |
| premium\_subscription\_count? | integer | the number of boosts this guild currently has |
| preferred\_locale | string | the preferred [locale](/developers/reference#locales) of a Community guild; used in server discovery and notices from Discord, and sent in interactions; defaults to "en-US" |
| public\_updates\_channel\_id | ?snowflake | the id of the channel where admins and moderators of Community guilds receive notices from Discord |
| max\_video\_channel\_users? | integer | the maximum amount of users in a video channel |
| max\_stage\_video\_channel\_users? | integer | the maximum amount of users in a stage video channel |
| approximate\_member\_count? | integer | approximate number of members in this guild, returned from the `GET /guilds/` and `/users/@me/guilds` endpoints when `with_counts` is `true` |
| approximate\_presence\_count? | integer | approximate number of non-offline members in this guild, returned from the `GET /guilds/` and `/users/@me/guilds` endpoints when `with_counts` is `true` |
| welcome\_screen? | [welcome screen](/developers/resources/guild#welcome-screen-object) object | the welcome screen of a Community guild, shown to new members, returned in an [Invite](/developers/resources/invite#invite-object)'s guild object |
| nsfw\_level | integer | [guild age-restriction level](/developers/resources/guild#guild-object-guild-nsfw-level) |
| stickers? | array of [sticker](/developers/resources/sticker#sticker-object) objects | custom guild stickers |
| premium\_progress\_bar\_enabled | boolean | whether the guild has the boost progress bar enabled |
| safety\_alerts\_channel\_id | ?snowflake | the id of the channel where admins and moderators of Community guilds receive safety alerts from Discord |
| incidents\_data | ?[incidents data](/developers/resources/guild#incidents-data-object) object | the incidents data for this guild |
\* These fields are only sent when using the [GET Current User Guilds](/developers/resources/user#get-current-user-guilds) endpoint and are relative to the requested user
\*\* This field is deprecated and is replaced by [channel.rtc\_region](/developers/resources/channel#channel-object-channel-structure)
###### Default Message Notification Level
| Key | Value | Description |
| -------------- | ----- | ---------------------------------------------------------------------------------- |
| ALL\_MESSAGES | 0 | members will receive notifications for all messages by default |
| ONLY\_MENTIONS | 1 | members will receive notifications only for messages that @mention them by default |
###### Explicit Content Filter Level
| Level | Integer | Description |
| ----------------------- | ------- | ----------------------------------------------------------- |
| DISABLED | 0 | media content will not be scanned |
| MEMBERS\_WITHOUT\_ROLES | 1 | media content sent by members without roles will be scanned |
| ALL\_MEMBERS | 2 | media content sent by all members will be scanned |
###### MFA Level
| Level | Integer | Description |
| -------- | ------- | ------------------------------------------------------- |
| NONE | 0 | guild has no MFA/2FA requirement for moderation actions |
| ELEVATED | 1 | guild has a 2FA requirement for moderation actions |
###### Verification Level
| Level | Integer | Description |
| ---------- | ------- | --------------------------------------------------------- |
| NONE | 0 | unrestricted |
| LOW | 1 | must have verified email on account |
| MEDIUM | 2 | must be registered on Discord for longer than 5 minutes |
| HIGH | 3 | must be a member of the server for longer than 10 minutes |
| VERY\_HIGH | 4 | must have a verified phone number |
###### Guild Age-Restriction Level
| Level | Value |
| --------------- | ----- |
| DEFAULT | 0 |
| EXPLICIT | 1 |
| SAFE | 2 |
| AGE\_RESTRICTED | 3 |
###### Premium Tier
| Level | Integer | Description |
| ------- | ------- | --------------------------------------------- |
| NONE | 0 | guild has not unlocked any Server Boost perks |
| TIER\_1 | 1 | guild has unlocked Server Boost level 1 perks |
| TIER\_2 | 2 | guild has unlocked Server Boost level 2 perks |
| TIER\_3 | 3 | guild has unlocked Server Boost level 3 perks |
###### System Channel Flags
| Flag | Value | Description |
| ------------------------------------------------------------- | -------- | ------------------------------------------------------------- |
| SUPPRESS\_JOIN\_NOTIFICATIONS | `1 << 0` | Suppress member join notifications |
| SUPPRESS\_PREMIUM\_SUBSCRIPTIONS | `1 << 1` | Suppress server boost notifications |
| SUPPRESS\_GUILD\_REMINDER\_NOTIFICATIONS | `1 << 2` | Suppress server setup tips |
| SUPPRESS\_JOIN\_NOTIFICATION\_REPLIES | `1 << 3` | Hide member join sticker reply buttons |
| SUPPRESS\_ROLE\_SUBSCRIPTION\_PURCHASE\_NOTIFICATIONS | `1 << 4` | Suppress role subscription purchase and renewal notifications |
| SUPPRESS\_ROLE\_SUBSCRIPTION\_PURCHASE\_NOTIFICATION\_REPLIES | `1 << 5` | Hide role subscription sticker reply buttons |
###### Guild Features
| Feature | Description |
| --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| ANIMATED\_BANNER | guild has access to set an animated guild banner image |
| ANIMATED\_ICON | guild has access to set an animated guild icon |
| APPLICATION\_COMMAND\_PERMISSIONS\_V2 | guild is using the [old permissions configuration behavior](/developers/change-log#upcoming-application-command-permission-changes) |
| AUTO\_MODERATION | guild has set up auto moderation rules |
| BANNER | guild has access to set a guild banner image |
| COMMUNITY | guild can enable welcome screen, Membership Screening, stage channels and discovery, and receives community updates |
| CREATOR\_MONETIZABLE\_PROVISIONAL | guild has enabled monetization |
| CREATOR\_STORE\_PAGE | guild has enabled the role subscription promo page |
| DEVELOPER\_SUPPORT\_SERVER | guild has been set as a support server on the App Directory |
| DISCOVERABLE | guild is able to be discovered in the directory |
| FEATURABLE | guild is able to be featured in the directory |
| INVITES\_DISABLED | guild has paused invites, preventing new users from joining |
| INVITE\_SPLASH | guild has access to set an invite splash background |
| MEMBER\_VERIFICATION\_GATE\_ENABLED | guild has enabled [Membership Screening](/developers/resources/guild#membership-screening-object) |
| MORE\_SOUNDBOARD | guild has increased custom soundboard sound slots |
| MORE\_STICKERS | guild has increased custom sticker slots |
| NEWS | guild has access to create announcement channels |
| PARTNERED | guild is partnered |
| PREVIEW\_ENABLED | guild can be previewed before joining via Membership Screening or the directory |
| RAID\_ALERTS\_DISABLED | guild has disabled alerts for join raids in the configured safety alerts channel |
| ROLE\_ICONS | guild is able to set role icons |
| ROLE\_SUBSCRIPTIONS\_AVAILABLE\_FOR\_PURCHASE | guild has role subscriptions that can be purchased |
| ROLE\_SUBSCRIPTIONS\_ENABLED | guild has enabled role subscriptions |
| SOUNDBOARD | guild has created soundboard sounds |
| TICKETED\_EVENTS\_ENABLED | guild has enabled ticketed events |
| VANITY\_URL | guild has access to set a vanity URL |
| VERIFIED | guild is verified |
| VIP\_REGIONS | guild has access to set 384kbps bitrate in voice (previously VIP voice servers) |
| WELCOME\_SCREEN\_ENABLED | guild has enabled the welcome screen |
| GUESTS\_ENABLED | guild has access to guest invites |
| GUILD\_TAGS | guild has access to set guild tags |
| ENHANCED\_ROLE\_COLORS | guild is able to set gradient colors to roles |
###### Mutable Guild Features
| Features | Required Permissions | Effects |
| ---------------------- | -------------------- | --------------------------------------------------------- |
| COMMUNITY | Administrator | Enables Community Features in the guild |
| DISCOVERABLE | Administrator\* | Enables discovery in the guild, making it publicly listed |
| INVITES\_DISABLED | Manage Guild | Pauses all invites/access to the server |
| RAID\_ALERTS\_DISABLED | Manage Guild | Disables alerts for join raids |
\* Server also must be passing all discovery requirements
###### Example Guild
```json theme={"system"}
{
"id": "197038439483310086",
"name": "Discord Testers",
"icon": "f64c482b807da4f539cff778d174971c",
"description": "The official place to report Discord Bugs!",
"splash": null,
"discovery_splash": null,
"features": [
"ANIMATED_ICON",
"VERIFIED",
"NEWS",
"VANITY_URL",
"DISCOVERABLE",
"MORE_EMOJI",
"INVITE_SPLASH",
"BANNER",
"COMMUNITY"
],
"emojis": [],
"banner": "9b6439a7de04f1d26af92f84ac9e1e4a",
"owner_id": "73193882359173120",
"application_id": null,
"region": null,
"afk_channel_id": null,
"afk_timeout": 300,
"system_channel_id": null,
"widget_enabled": true,
"widget_channel_id": null,
"verification_level": 3,
"roles": [],
"default_message_notifications": 1,
"mfa_level": 1,
"explicit_content_filter": 2,
"max_presences": 40000,
"max_members": 250000,
"vanity_url_code": "discord-testers",
"premium_tier": 3,
"premium_subscription_count": 33,
"system_channel_flags": 0,
"preferred_locale": "en-US",
"rules_channel_id": "441688182833020939",
"public_updates_channel_id": "281283303326089216",
"safety_alerts_channel_id": "281283303326089216"
}
```
### Unavailable Guild Object
A partial [guild](/developers/resources/guild#guild-object) object. Represents an Offline Guild, or a Guild whose information has not been provided through [Guild Create](/developers/events/gateway-events#guild-create) events during the Gateway connect.
###### Example Unavailable Guild
```json theme={"system"}
{
"id": "41771983423143937",
"unavailable": true
}
```
### Guild Preview Object
###### Guild Preview Structure
| Field | Type | Description |
| ---------------------------- | ----------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
| id | snowflake | guild id |
| name | string | guild name (2-100 characters) |
| icon | ?string | [icon hash](/developers/reference#image-formatting) |
| splash | ?string | [splash hash](/developers/reference#image-formatting) |
| discovery\_splash | ?string | [discovery splash hash](/developers/reference#image-formatting) |
| emojis | array of [emoji](/developers/resources/emoji#emoji-object) objects | custom guild emojis |
| features | array of [guild feature](/developers/resources/guild#guild-object-guild-features) strings | enabled guild features |
| approximate\_member\_count | integer | approximate number of members in this guild |
| approximate\_presence\_count | integer | approximate number of online members in this guild |
| description | ?string | the description for the guild |
| stickers | array of [sticker](/developers/resources/sticker#sticker-object) objects | custom guild stickers |
###### Example Guild Preview
```json theme={"system"}
{
"id": "197038439483310086",
"name": "Discord Testers",
"icon": "f64c482b807da4f539cff778d174971c",
"splash": null,
"discovery_splash": null,
"emojis": [],
"features": [
"DISCOVERABLE",
"VANITY_URL",
"ANIMATED_ICON",
"INVITE_SPLASH",
"NEWS",
"COMMUNITY",
"BANNER",
"VERIFIED",
"MORE_EMOJI"
],
"approximate_member_count": 60814,
"approximate_presence_count": 20034,
"description": "The official place to report Discord Bugs!",
"stickers": []
}
```
### Guild Widget Settings Object
###### Guild Widget Settings Structure
| Field | Type | Description |
| ----------- | ---------- | ----------------------------- |
| enabled | boolean | whether the widget is enabled |
| channel\_id | ?snowflake | the widget channel id |
###### Example Guild Widget Settings
```json theme={"system"}
{
"enabled": true,
"channel_id": "41771983444115456"
}
```
### Guild Widget Object
###### Guild Widget Structure
| Field | Type | Description |
| --------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
| id | snowflake | guild id |
| name | string | guild name (2-100 characters) |
| instant\_invite | ?string | instant invite for the guilds specified widget invite channel |
| channels | array of partial [channel](/developers/resources/channel#channel-object) objects | voice and stage channels which are accessible by @everyone |
| members | array of partial [user](/developers/resources/user#user-object) objects | special widget user objects that includes users presence (Limit 100) |
| presence\_count | integer | number of online members in this guild |
The fields `id`, `discriminator` and `avatar` are anonymized to prevent abuse.
###### Example Guild Widget
```json theme={"system"}
{
"id": "290926798626999250",
"name": "Test Server",
"instant_invite": "https://discord.com/invite/abcdefg",
"channels": [
{
"id": "705216630279993882",
"name": "elephant",
"position": 2
},
{
"id": "669583461748992190",
"name": "groovy-music",
"position": 1
}
],
"members": [
{
"id": "0",
"username": "1234",
"discriminator": "0000",
"avatar": null,
"status": "online",
"avatar_url": "https://cdn.discordapp.com/widget-avatars/FfvURgcr3Za92K3JtoCppqnYMppMDc5B-Rll74YrGCU/C-1DyBZPQ6t5q2RuATFuMFgq0_uEMZVzd_6LbGN_uJKvZflobA9diAlTjhf6CAESLLeTuu4dLuHFWOb_PNLteooNfhC4C6k5QgAGuxEOP12tVVVCvX6t64k14PMXZrGTDq8pWZhukP40Wg"
}
],
"presence_count": 1
}
```
### Guild Member Object
###### Guild Member Structure
| Field | Type | Description |
| ------------------------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| user? | [user](/developers/resources/user#user-object) object | the user this guild member represents |
| nick? | ?string | this user's guild nickname |
| avatar? | ?string | the member's [guild avatar hash](/developers/reference#image-formatting) |
| banner? | ?string | the member's [guild banner hash](/developers/reference#image-formatting) |
| roles | array of snowflakes | array of [role](/developers/topics/permissions#role-object) object ids |
| joined\_at | ?ISO8601 timestamp | when the user joined the guild |
| premium\_since? | ?ISO8601 timestamp | when the user started [boosting](https://support.discord.com/hc/en-us/articles/360028038352-Server-Boosting-) the guild |
| deaf | boolean | whether the user is deafened in voice channels |
| mute | boolean | whether the user is muted in voice channels |
| flags | integer | [guild member flags](/developers/resources/guild#guild-member-object-guild-member-flags) represented as a bit set, defaults to `0` |
| pending? | boolean | whether the user has not yet passed the guild's [Membership Screening](/developers/resources/guild#membership-screening-object) requirements |
| permissions? | string | total permissions of the member in the channel, including overwrites, returned when in the interaction object |
| communication\_disabled\_until? | ?ISO8601 timestamp | when the user's [timeout](https://support.discord.com/hc/en-us/articles/4413305239191-Time-Out-FAQ) will expire and the user will be able to communicate in the guild again, null or a time in the past if the user is not timed out |
| avatar\_decoration\_data? | ?[avatar decoration data](/developers/resources/user#avatar-decoration-data-object) object | data for the member's guild avatar decoration |
| collectibles? | ?[collectibles](/developers/resources/user#collectibles) object | data for the member's collectibles |
The field `user` won't be included in the member object attached to `MESSAGE_CREATE` and `MESSAGE_UPDATE` gateway events.
In `GUILD_` events, `pending` will always be included as true or false. In non `GUILD_` events which can only be triggered by non-`pending` users, `pending` will not be included.
Member objects retrieved from `VOICE_STATE_UPDATE` events will have `joined_at` set as `null` if the member was invited as a guest.
###### Example Guild Member
```json theme={"system"}
{
"user": {},
"nick": "NOT API SUPPORT",
"avatar": null,
"banner": null,
"roles": [],
"joined_at": "2015-04-26T06:26:56.936000+00:00",
"deaf": false,
"mute": false
}
```
###### Guild Member Flags
| Flag | Value | Description | Editable |
| ---------------------------------- | --------- | ---------------------------------------------------------------------------- | -------- |
| DID\_REJOIN | `1 << 0` | Member has left and rejoined the guild | false |
| COMPLETED\_ONBOARDING | `1 << 1` | Member has completed onboarding | false |
| BYPASSES\_VERIFICATION | `1 << 2` | Member is exempt from guild verification requirements | true |
| STARTED\_ONBOARDING | `1 << 3` | Member has started onboarding | false |
| IS\_GUEST | `1 << 4` | Member is a guest and can only access the voice channel they were invited to | false |
| STARTED\_HOME\_ACTIONS | `1 << 5` | Member has started Server Guide new member actions | false |
| COMPLETED\_HOME\_ACTIONS | `1 << 6` | Member has completed Server Guide new member actions | false |
| AUTOMOD\_QUARANTINED\_USERNAME | `1 << 7` | Member's username, display name, or nickname is blocked by AutoMod | false |
| DM\_SETTINGS\_UPSELL\_ACKNOWLEDGED | `1 << 9` | Member has dismissed the DM settings upsell | false |
| AUTOMOD\_QUARANTINED\_GUILD\_TAG | `1 << 10` | Member's guild tag is blocked by AutoMod | false |
BYPASSES\_VERIFICATION allows a member who does not meet verification requirements to participate in a server.
### Integration Object
###### Integration Structure
| Field | Type | Description |
| ------------------------- | ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| id | snowflake | integration id |
| name | string | integration name |
| type | string | integration type (twitch, youtube, discord, or guild\_subscription) |
| enabled | boolean | is this integration enabled |
| syncing? \* | boolean | is this integration syncing |
| role\_id? \* | snowflake | id that this integration uses for "subscribers" |
| enable\_emoticons? \* | boolean | whether emoticons should be synced for this integration (twitch only currently) |
| expire\_behavior? \* | [integration expire behavior](/developers/resources/guild#integration-object-integration-expire-behaviors) | the behavior of expiring subscribers |
| expire\_grace\_period? \* | integer | the grace period (in days) before expiring subscribers |
| user? | [user](/developers/resources/user#user-object) object | user for this integration |
| account | [account](/developers/resources/guild#integration-account-object) object | integration account information |
| synced\_at? \* | ISO8601 timestamp | when this integration was last synced |
| subscriber\_count? \* | integer | how many subscribers this integration has |
| revoked? \* | boolean | has this integration been revoked |
| application? | [application](/developers/resources/guild#integration-application-object) object | The bot/OAuth2 application for discord integrations |
| scopes? | array of [OAuth2 scopes](/developers/topics/oauth2#shared-resources-oauth2-scopes) | the scopes the application has been authorized for |
\* These fields are not provided for discord bot integrations.
Some older integrations may not have an attached user.
###### Integration Expire Behaviors
| Value | Name |
| ----- | ----------- |
| 0 | Remove role |
| 1 | Kick |
### Integration Account Object
###### Integration Account Structure
| Field | Type | Description |
| ----- | ------ | ------------------- |
| id | string | id of the account |
| name | string | name of the account |
### Integration Application Object
###### Integration Application Structure
| Field | Type | Description |
| ----------- | ----------------------------------------------------- | ------------------------------------------------------------------ |
| id | snowflake | the id of the app |
| name | string | the name of the app |
| icon | ?string | the [icon hash](/developers/reference#image-formatting) of the app |
| description | string | the description of the app |
| bot? | [user](/developers/resources/user#user-object) object | the bot associated with this application |
### Ban Object
###### Ban Structure
| Field | Type | Description |
| ------ | ----------------------------------------------------- | ---------------------- |
| reason | ?string | the reason for the ban |
| user | [user](/developers/resources/user#user-object) object | the banned user |
###### Example Ban
```json theme={"system"}
{
"reason": "mentioning b1nzy",
"user": {
"username": "Mason",
"discriminator": "9999",
"id": "53908099506183680",
"avatar": "a_bab14f271d565501444b2ca3be944b25",
"public_flags": 131141
}
}
```
### Welcome Screen Object
###### Welcome Screen Structure
| Field | Type | Description |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- |
| description | ?string | the server description shown in the welcome screen |
| welcome\_channels | array of [welcome screen channel](/developers/resources/guild#welcome-screen-object-welcome-screen-channel-structure) objects | the channels shown in the welcome screen, up to 5 |
###### Welcome Screen Channel Structure
| Field | Type | Description |
| ----------- | ---------- | ----------------------------------------------------------------------------------------- |
| channel\_id | snowflake | the channel's id |
| description | string | the description shown for the channel |
| emoji\_id | ?snowflake | the [emoji id](/developers/reference#image-formatting), if the emoji is custom |
| emoji\_name | ?string | the emoji name if custom, the unicode character if standard, or `null` if no emoji is set |
###### Example Welcome Screen
```json theme={"system"}
{
"description": "Discord Developers is a place to learn about Discord's API, bots, and SDKs and integrations. This is NOT a general Discord support server.",
"welcome_channels": [
{
"channel_id": "697138785317814292",
"description": "Follow for official Discord API updates",
"emoji_id": null,
"emoji_name": "📡"
},
{
"channel_id": "697236247739105340",
"description": "Get help with Bot Verifications",
"emoji_id": null,
"emoji_name": "📸"
},
{
"channel_id": "697489244649816084",
"description": "Create amazing things with Discord's API",
"emoji_id": null,
"emoji_name": "🔬"
},
{
"channel_id": "613425918748131338",
"description": "Integrate Discord into your game",
"emoji_id": null,
"emoji_name": "🎮"
},
{
"channel_id": "646517734150242346",
"description": "Find more places to help you on your quest",
"emoji_id": null,
"emoji_name": "🔦"
}
]
}
```
### Guild Onboarding Object
Represents the [onboarding](https://support.discord.com/hc/en-us/articles/11074987197975-Community-Onboarding-FAQ) flow for a guild.
###### Guild Onboarding Structure
| Field | Type | Description |
| --------------------- | --------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| guild\_id | snowflake | ID of the guild this onboarding is part of |
| prompts | array of [onboarding prompt](/developers/resources/guild#guild-onboarding-object-onboarding-prompt-structure) objects | Prompts shown during onboarding and in customize community |
| default\_channel\_ids | array of snowflakes | Channel IDs that members get opted into automatically |
| enabled | boolean | Whether onboarding is enabled in the guild |
| mode | [onboarding mode](/developers/resources/guild#guild-onboarding-object-onboarding-mode) | Current mode of onboarding |
###### Onboarding Prompt Structure
| Field | Type | Description |
| -------------- | ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| id | snowflake | ID of the prompt |
| type | [prompt type](/developers/resources/guild#guild-onboarding-object-prompt-types) | Type of prompt |
| options | array of [prompt option](/developers/resources/guild#guild-onboarding-object-prompt-option-structure) objects | Options available within the prompt |
| title | string | Title of the prompt |
| single\_select | boolean | Indicates whether users are limited to selecting one option for the prompt |
| required | boolean | Indicates whether the prompt is required before a user completes the onboarding flow |
| in\_onboarding | boolean | Indicates whether the prompt is present in the onboarding flow. If `false`, the prompt will only appear in the Channels & Roles tab |
###### Prompt Option Structure
| Field | Type | Description |
| ---------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| id | snowflake | ID of the prompt option |
| channel\_ids | array of snowflakes | IDs for channels a member is added to when the option is selected |
| role\_ids | array of snowflakes | IDs for [roles](/developers/topics/permissions#role-object) assigned to a member when the option is selected |
| emoji? | [emoji](/developers/resources/emoji#emoji-object) object | Emoji of the option (see below) |
| emoji\_id? | snowflake | Emoji ID of the option (see below) |
| emoji\_name? | string | Emoji name of the option (see below) |
| emoji\_animated? | boolean | Whether the emoji is animated (see below) |
| title | string | Title of the option |
| description | ?string | Description of the option |
When creating or updating a prompt option, the `emoji_id`, `emoji_name`, and `emoji_animated` fields must be used instead of the emoji object.
###### Onboarding Mode
Defines the criteria used to satisfy Onboarding constraints that are required for enabling.
| Name | Value | Description |
| -------------------- | ----- | --------------------------------------------------------- |
| ONBOARDING\_DEFAULT | 0 | Counts only Default Channels towards constraints |
| ONBOARDING\_ADVANCED | 1 | Counts Default Channels and Questions towards constraints |
###### Prompt Types
| Name | Value |
| ---------------- | ----- |
| MULTIPLE\_CHOICE | 0 |
| DROPDOWN | 1 |
###### Example Guild Onboarding
```json theme={"system"}
{
"guild_id": "960007075288915998",
"prompts": [
{
"id": "1067461047608422473",
"title": "What do you want to do in this community?",
"options": [
{
"id": "1067461047608422476",
"title": "Chat with Friends",
"description": "",
"emoji": {
"id": "1070002302032826408",
"name": "chat",
"animated": false
},
"role_ids": [],
"channel_ids": [
"962007075288916001"
]
},
{
"id": "1070004843541954678",
"title": "Get Gud",
"description": "We have excellent teachers!",
"emoji": {
"id": null,
"name": "😀",
"animated": false
},
"role_ids": [
"982014491980083211"
],
"channel_ids": []
}
],
"single_select": false,
"required": false,
"in_onboarding": true,
"type": 0
}
],
"default_channel_ids": [
"998678771706110023",
"998678693058719784",
"1070008122577518632",
"998678764340912138",
"998678704446263309",
"998678683592171602",
"998678699715067986"
],
"enabled": true
}
```
### Membership Screening Object
In guilds with [Membership Screening](https://support.discord.com/hc/en-us/articles/1500000466882) enabled, when a member joins, [Guild Member Add](/developers/events/gateway-events#guild-member-add) will be emitted but they will initially be restricted from doing any actions in the guild, and `pending` will be true in the [member object](/developers/resources/guild#guild-member-object). When the member completes the screening, [Guild Member Update](/developers/events/gateway-events#guild-member-update) will be emitted and `pending` will be false.
We are making significant changes to the Membership Screening API specifically related to getting and editing the Membership Screening object. Long story short is that it can be improved. As such, we have removed those documentation. There will **not be** any changes to how pending members work, as outlined above. That behavior will stay the same.
### Incidents Data Object
###### Incidents Data Structure
| Field | Type | Description |
| ------------------------ | ------------------ | -------------------------------------- |
| invites\_disabled\_until | ?ISO8601 timestamp | when invites get enabled again |
| dms\_disabled\_until | ?ISO8601 timestamp | when direct messages get enabled again |
| dm\_spam\_detected\_at? | ?ISO8601 timestamp | when the dm spam was detected |
| raid\_detected\_at? | ?ISO8601 timestamp | when the raid was detected |
###### Example Incidents Data
```json theme={"system"}
{
"invites_disabled_until": "2023-09-01T14:48:02.222000+00:00",
"dms_disabled_until": null
}
```
## Get Guild
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)
Returns the [guild](/developers/resources/guild#guild-object) object for the given id. If `with_counts` is set to `true`, this endpoint will also return `approximate_member_count` and `approximate_presence_count` for the guild.
###### Query String Params
| Field | Type | Description | Required | Default |
| ------------- | ------------------------------------------------------ | ----------------------------------------------------------------------------- | -------- | ------- |
| with\_counts? | [boolean](/developers/reference#boolean-query-strings) | when `true`, will return approximate member and presence counts for the guild | false | false |
###### Example Response
```json theme={"system"}
{
"id": "2909267986263572999",
"name": "Mason's Test Server",
"icon": "389030ec9db118cb5b85a732333b7c98",
"description": null,
"splash": "75610b05a0dd09ec2c3c7df9f6975ea0",
"discovery_splash": null,
"approximate_member_count": 2,
"approximate_presence_count": 2,
"features": [
"INVITE_SPLASH",
"VANITY_URL",
"COMMERCE",
"BANNER",
"NEWS",
"VERIFIED",
"VIP_REGIONS"
],
"emojis": [
{
"name": "ultrafastparrot",
"roles": [],
"id": "393564762228785161",
"require_colons": true,
"managed": false,
"animated": true,
"available": true
}
],
"banner": "5c3cb8d1bc159937fffe7e641ec96ca7",
"owner_id": "53908232506183680",
"application_id": null,
"region": null,
"afk_channel_id": null,
"afk_timeout": 300,
"system_channel_id": null,
"widget_enabled": true,
"widget_channel_id": "639513352485470208",
"verification_level": 0,
"roles": [
{
"id": "2909267986263572999",
"name": "@everyone",
"permissions": "49794752",
"position": 0,
"color": 0,
"colors": {
"primary_color": 0,
"secondary_color": null,
"tertiary_color": null
},
"hoist": false,
"managed": false,
"mentionable": false
}
],
"default_message_notifications": 1,
"mfa_level": 0,
"explicit_content_filter": 0,
"max_presences": null,
"max_members": 250000,
"max_video_channel_users": 25,
"vanity_url_code": "no",
"premium_tier": 0,
"premium_subscription_count": 0,
"system_channel_flags": 0,
"preferred_locale": "en-US",
"rules_channel_id": null,
"public_updates_channel_id": null,
"safety_alerts_channel_id": null
}
```
## Get Guild Preview
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/preview
Returns the [guild preview](/developers/resources/guild#guild-preview-object) object for the given id.
If the user is not in the guild, then the guild must be [discoverable](/developers/resources/guild#guild-object-guild-features).
## Modify Guild
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)
Modify a guild's settings. Requires the `MANAGE_GUILD` permission. Returns the updated [guild](/developers/resources/guild#guild-object) object on success. Fires a [Guild Update](/developers/events/gateway-events#guild-update) Gateway event.
All parameters to this endpoint are optional.
This endpoint supports the `X-Audit-Log-Reason` header.
Attempting to add or remove the `COMMUNITY` [guild feature](/developers/resources/guild#guild-object-guild-features) requires the `ADMINISTRATOR` permission.
###### JSON Params
| Field | Type | Description |
| ------------------------------- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name | string | guild name |
| region | ?string | guild [voice region](/developers/resources/voice#voice-region-object) id (deprecated) |
| verification\_level | ?integer | [verification level](/developers/resources/guild#guild-object-verification-level) |
| default\_message\_notifications | ?integer | default [message notification level](/developers/resources/guild#guild-object-default-message-notification-level) |
| explicit\_content\_filter | ?integer | [explicit content filter level](/developers/resources/guild#guild-object-explicit-content-filter-level) |
| afk\_channel\_id | ?snowflake | id for afk channel |
| afk\_timeout | integer | afk timeout in seconds, can be set to: 60, 300, 900, 1800, 3600 |
| icon | ?[image data](/developers/reference#image-data) | base64 1024x1024 png/jpeg/gif image for the guild icon (can be animated gif when the server has the `ANIMATED_ICON` feature) |
| splash | ?[image data](/developers/reference#image-data) | base64 16:9 png/jpeg image for the guild splash (when the server has the `INVITE_SPLASH` feature) |
| discovery\_splash | ?[image data](/developers/reference#image-data) | base64 16:9 png/jpeg image for the guild discovery splash (when the server has the `DISCOVERABLE` feature) |
| banner | ?[image data](/developers/reference#image-data) | base64 16:9 png/jpeg image for the guild banner (when the server has the `BANNER` feature; can be animated gif when the server has the `ANIMATED_BANNER` feature) |
| system\_channel\_id | ?snowflake | the id of the channel where guild notices such as welcome messages and boost events are posted |
| system\_channel\_flags | integer | [system channel flags](/developers/resources/guild#guild-object-system-channel-flags) |
| rules\_channel\_id | ?snowflake | the id of the channel where Community guilds display rules and/or guidelines |
| public\_updates\_channel\_id | ?snowflake | the id of the channel where admins and moderators of Community guilds receive notices from Discord |
| preferred\_locale | ?string | the preferred [locale](/developers/reference#locales) of a Community guild used in server discovery and notices from Discord; defaults to "en-US" |
| features | array of [guild feature](/developers/resources/guild#guild-object-guild-features) strings | enabled guild features |
| description | ?string | the description for the guild |
| premium\_progress\_bar\_enabled | boolean | whether the guild's boost progress bar should be enabled |
| safety\_alerts\_channel\_id | ?snowflake | the id of the channel where admins and moderators of Community guilds receive safety alerts from Discord |
## Get Guild Channels
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/channels
Returns a list of guild [channel](/developers/resources/channel#channel-object) objects. Does not include threads.
## Create Guild Channel
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/channels
Create a new [channel](/developers/resources/channel#channel-object) object for the guild. Requires the `MANAGE_CHANNELS` permission. If setting permission overwrites, only permissions your bot has in the guild can be allowed/denied. Setting `MANAGE_ROLES` permission in channels is only possible for guild administrators. Returns the new [channel](/developers/resources/channel#channel-object) object on success. Fires a [Channel Create](/developers/events/gateway-events#channel-create) Gateway event.
All parameters to this endpoint are optional and nullable excluding `name`.
This endpoint supports the `X-Audit-Log-Reason` header.
###### JSON Params
| Field | Type | Description | Channel Type |
| --------------------------------------- | ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- |
| name | string | channel name (1-100 characters) | All |
| type | integer | the [type of channel](/developers/resources/channel#channel-object-channel-types) | All |
| topic | string | channel topic (0-1024 characters) | Text, Announcement, Forum, Media |
| bitrate\* | integer | the bitrate (in bits per second) of the voice or stage channel; min 8000 | Voice, Stage |
| user\_limit | integer | the user limit of the voice channel | Voice, Stage |
| rate\_limit\_per\_user | integer | amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission `BYPASS_SLOWMODE`, are unaffected | Text, Voice, Stage, Forum, Media |
| position | integer | sorting position of the channel (channels with the same position are sorted by id) | All |
| permission\_overwrites\*\* | array of partial [overwrite](/developers/resources/channel#overwrite-object) objects | the channel's permission overwrites | All |
| parent\_id | snowflake | id of the parent category for a channel | Text, Voice, Announcement, Stage, Forum, Media |
| nsfw | boolean | whether the channel is [age-restricted](https://support.discord.com/hc/en-us/articles/115000084051) | Text, Voice, Announcement, Stage, Forum |
| rtc\_region | string | channel [voice region](/developers/resources/voice#voice-region-object) id of the voice or stage channel, automatic when set to null | Voice, Stage |
| video\_quality\_mode | integer | the camera [video quality mode](/developers/resources/channel#channel-object-video-quality-modes) of the voice channel | Voice, Stage |
| default\_auto\_archive\_duration | integer | the default duration that the clients use (not the API) for newly created threads in the channel, in minutes, to automatically archive the thread after recent activity | Text, Announcement, Forum, Media |
| default\_reaction\_emoji | [default reaction](/developers/resources/channel#default-reaction-object) object | emoji to show in the add reaction button on a thread in a `GUILD_FORUM` or a `GUILD_MEDIA` channel | Forum, Media |
| available\_tags | array of [tag](/developers/resources/channel#forum-tag-object) objects | set of tags that can be used in a `GUILD_FORUM` or a `GUILD_MEDIA` channel | Forum, Media |
| default\_sort\_order | integer | the [default sort order type](/developers/resources/channel#channel-object-sort-order-types) used to order posts in `GUILD_FORUM` and `GUILD_MEDIA` channels | Forum, Media |
| default\_forum\_layout | integer | the [default forum layout view](/developers/resources/channel#channel-object-forum-layout-types) used to display posts in `GUILD_FORUM` channels | Forum |
| default\_thread\_rate\_limit\_per\_user | integer | the initial `rate_limit_per_user` to set on newly created threads in a channel. this field is copied to the thread at creation time and does not live update. | Text, Announcement, Forum, Media |
\* For voice channels, normal servers can set bitrate up to 96000, servers with Boost level 1 can set up to 128000, servers with Boost level 2 can set up to 256000, and servers with Boost level 3 or the `VIP_REGIONS` [guild feature](/developers/resources/guild#guild-object-guild-features) can set up to 384000. For stage channels, bitrate can be set up to 64000.
\*\* In each overwrite object, the `allow` and `deny` keys can be omitted or set to `null`, which both default to `"0"`.
## Modify Guild Channel Positions
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/channels
Modify the positions of a set of [channel](/developers/resources/channel#channel-object) objects for the guild. Requires `MANAGE_CHANNELS` permission. Returns a 204 empty response on success. Fires multiple [Channel Update](/developers/events/gateway-events#channel-update) Gateway events.
Only channels to be modified are required.
This endpoint takes a JSON array of parameters in the following format:
###### JSON Params
| Field | Type | Description |
| ------------------ | ---------- | ---------------------------------------------------------------------------------- |
| id | snowflake | channel id |
| position? | ?integer | sorting position of the channel (channels with the same position are sorted by id) |
| lock\_permissions? | ?boolean | syncs the permission overwrites with the new parent, if moving to a new category |
| parent\_id? | ?snowflake | the new parent ID for the channel that is moved |
## List Active Guild Threads
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/threads/active
Returns all active threads in the guild, including public and private threads. Threads are ordered by their `id`, in descending order.
###### Response Body
| Field | Type | Description |
| ------- | ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| threads | array of [channel](/developers/resources/channel#channel-object) objects | the active threads |
| members | array of [thread members](/developers/resources/channel#thread-member-object) objects | a thread member object for each returned thread the current user has joined |
## Get Guild Member
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/members/[\{user.id}](/developers/resources/user#user-object)
Returns a [guild member](/developers/resources/guild#guild-member-object) object for the specified user.
## List Guild Members
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/members
Returns a list of [guild member](/developers/resources/guild#guild-member-object) objects that are members of the guild.
This endpoint requires the `GUILD_MEMBERS` [Privileged Intent](/developers/events/gateway#privileged-intents).
All parameters to this endpoint are optional.
###### Query String Params
| Field | Type | Description | Default |
| ----- | --------- | ---------------------------------------- | ------- |
| limit | integer | max number of members to return (1-1000) | 1 |
| after | snowflake | the highest user id in the previous page | 0 |
## Search Guild Members
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/members/search
Returns a list of [guild member](/developers/resources/guild#guild-member-object) objects whose username or nickname starts with a provided string.
All parameters to this endpoint except for `query` are optional
###### Query String Params
| Field | Type | Description | Default |
| ----- | ------- | ---------------------------------------------------------- | ------- |
| query | string | Query string to match username(s) and nickname(s) against. | |
| limit | integer | max number of members to return (1-1000) | 1 |
## Add Guild Member
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/members/[\{user.id}](/developers/resources/user#user-object)
Adds a user to the guild, provided you have a valid oauth2 access token for the user with the `guilds.join` scope. Returns a 201 Created with the [guild member](/developers/resources/guild#guild-member-object) as the body, or 204 No Content if the user is already a member of the guild. Fires a [Guild Member Add](/developers/events/gateway-events#guild-member-add) Gateway event.
For guilds with [Membership Screening](/developers/resources/guild#membership-screening-object) enabled, this endpoint will default to adding new members as `pending` in the [guild member object](/developers/resources/guild#guild-member-object). Members that are `pending` will have to complete membership screening before they become full members that can talk.
All parameters to this endpoint except for `access_token` are optional.
The Authorization header must be a Bot token (belonging to the same application used for authorization), and the bot must be a member of the guild with `CREATE_INSTANT_INVITE` permission.
###### JSON Params
| Field | Type | Description | Permission |
| ------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------ | ----------------- |
| access\_token | string | an oauth2 access token granted with the `guilds.join` to the bot's application for the user you want to add to the guild | |
| nick | string | value to set user's nickname to | MANAGE\_NICKNAMES |
| roles | array of snowflakes | array of role ids the member is assigned | MANAGE\_ROLES |
| mute | boolean | whether the user is muted in voice channels | MUTE\_MEMBERS |
| deaf | boolean | whether the user is deafened in voice channels | DEAFEN\_MEMBERS |
## Modify Guild Member
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/members/[\{user.id}](/developers/resources/user#user-object)
Modify attributes of a [guild member](/developers/resources/guild#guild-member-object). Returns a 200 OK with the [guild member](/developers/resources/guild#guild-member-object) as the body. Fires a [Guild Member Update](/developers/events/gateway-events#guild-member-update) Gateway event. If the `channel_id` is set to null, this will force the target user to be disconnected from voice.
All parameters to this endpoint are optional and nullable. When moving members to channels, the API user *must* have permissions to both connect to the channel and have the `MOVE_MEMBERS` permission.
This endpoint supports the `X-Audit-Log-Reason` header.
###### JSON Params
| Field | Type | Description | Permission |
| ------------------------------ | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------- |
| nick | string | value to set user's nickname to | MANAGE\_NICKNAMES |
| roles | array of snowflakes | array of role ids the member is assigned | MANAGE\_ROLES |
| mute | boolean | whether the user is muted in voice channels. Will throw a 400 error if the user is not in a voice channel | MUTE\_MEMBERS |
| deaf | boolean | whether the user is deafened in voice channels. Will throw a 400 error if the user is not in a voice channel | DEAFEN\_MEMBERS |
| channel\_id | snowflake | id of channel to move user to (if they are connected to voice) | MOVE\_MEMBERS |
| communication\_disabled\_until | ISO8601 timestamp | when the user's [timeout](https://support.discord.com/hc/en-us/articles/4413305239191-Time-Out-FAQ) will expire and the user will be able to communicate in the guild again (up to 28 days in the future), set to null to remove timeout. Will throw a 403 error if the user has the ADMINISTRATOR permission or is the owner of the guild | MODERATE\_MEMBERS |
| flags | integer | [guild member flags](/developers/resources/guild#guild-member-object-guild-member-flags) | MANAGE\_GUILD or MANAGE\_ROLES or (MODERATE\_MEMBERS and KICK\_MEMBERS and BAN\_MEMBERS) |
## Modify Current Member
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/members/@me
Modifies the current member in a guild. Returns a 200 with the updated member object on success. Fires a [Guild Member Update](/developers/events/gateway-events#guild-member-update) Gateway event.
This endpoint supports the `X-Audit-Log-Reason` header.
###### JSON Params
| Field | Type | Description | Permission |
| ------- | ------- | ------------------------------------------------------------------------ | ---------------- |
| nick? | ?string | value to set user's nickname to | CHANGE\_NICKNAME |
| banner? | ?string | [data URI base64 encoded](/developers/reference#image-data) banner image | |
| avatar? | ?string | [data URI base64 encoded](/developers/reference#image-data) avatar image | |
| bio? | ?string | guild member bio | |
## Modify Current User Nick
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/members/@me/nick
Deprecated in favor of [Modify Current Member](/developers/resources/guild#modify-current-member).
Modifies the nickname of the current user in a guild. Returns a 200 with the nickname on success. Fires a [Guild Member Update](/developers/events/gateway-events#guild-member-update) Gateway event.
This endpoint supports the `X-Audit-Log-Reason` header.
###### JSON Params
| Field | Type | Description | Permission |
| ----- | ------- | ------------------------------- | ---------------- |
| nick? | ?string | value to set user's nickname to | CHANGE\_NICKNAME |
## Add Guild Member Role
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/members/[\{user.id}](/developers/resources/user#user-object)/roles/[\{role.id}](/developers/topics/permissions#role-object)
Adds a role to a [guild member](/developers/resources/guild#guild-member-object). Requires the `MANAGE_ROLES` permission. Returns a 204 empty response on success. Fires a [Guild Member Update](/developers/events/gateway-events#guild-member-update) Gateway event.
This endpoint supports the `X-Audit-Log-Reason` header.
## Remove Guild Member Role
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/members/[\{user.id}](/developers/resources/user#user-object)/roles/[\{role.id}](/developers/topics/permissions#role-object)
Removes a role from a [guild member](/developers/resources/guild#guild-member-object). Requires the `MANAGE_ROLES` permission. Returns a 204 empty response on success. Fires a [Guild Member Update](/developers/events/gateway-events#guild-member-update) Gateway event.
This endpoint supports the `X-Audit-Log-Reason` header.
## Remove Guild Member
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/members/[\{user.id}](/developers/resources/user#user-object)
Remove a member from a guild. Requires `KICK_MEMBERS` permission. Returns a 204 empty response on success. Fires a [Guild Member Remove](/developers/events/gateway-events#guild-member-remove) Gateway event.
This endpoint supports the `X-Audit-Log-Reason` header.
## Get Guild Bans
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/bans
Returns a list of [ban](/developers/resources/guild#ban-object) objects for the users banned from this guild. Requires the `BAN_MEMBERS` permission.
###### Query String Params
| Field | Type | Description | Default |
| ---------- | --------- | ---------------------------------------------- | ------- |
| limit? | number | number of users to return (up to maximum 1000) | 1000 |
| before? \* | snowflake | consider only users before given user id | null |
| after? \* | snowflake | consider only users after given user id | null |
\* Provide a user id to `before` and `after` for pagination. Users will always be returned in ascending order by `user.id`. If both `before` and `after` are provided, only `before` is respected.
## Get Guild Ban
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/bans/[\{user.id}](/developers/resources/user#user-object)
Returns a [ban](/developers/resources/guild#ban-object) object for the given user or a 404 not found if the ban cannot be found. Requires the `BAN_MEMBERS` permission.
## Create Guild Ban
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/bans/[\{user.id}](/developers/resources/user#user-object)
Create a guild ban, and optionally delete previous messages sent by the banned user. Requires the `BAN_MEMBERS` permission. Returns a 204 empty response on success. Fires a [Guild Ban Add](/developers/events/gateway-events#guild-ban-add) Gateway event.
This endpoint supports the `X-Audit-Log-Reason` header.
###### JSON Params
| Field | Type | Description | Default |
| ------------------------- | ------- | ----------------------------------------------------------------------- | ------- |
| delete\_message\_days? | integer | number of days to delete messages for (0-7) (deprecated) | 0 |
| delete\_message\_seconds? | integer | number of seconds to delete messages for, between 0 and 604800 (7 days) | 0 |
## Remove Guild Ban
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/bans/[\{user.id}](/developers/resources/user#user-object)
Remove the ban for a user. Requires the `BAN_MEMBERS` permissions. Returns a 204 empty response on success. Fires a [Guild Ban Remove](/developers/events/gateway-events#guild-ban-remove) Gateway event.
This endpoint supports the `X-Audit-Log-Reason` header.
## Bulk Guild Ban
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/bulk-ban
Ban up to 200 users from a guild, and optionally delete previous messages sent by the banned users. Requires both the `BAN_MEMBERS` and `MANAGE_GUILD` permissions. Returns a 200 response on success, including the fields `banned_users` with the IDs of the banned users and `failed_users` with IDs that could not be banned or were already banned.
This endpoint supports the `X-Audit-Log-Reason` header.
###### JSON Params
| Field | Type | Description | Default |
| ------------------------- | ------------------- | ----------------------------------------------------------------------- | ------- |
| user\_ids | array of snowflakes | list of user ids to ban (max 200) | |
| delete\_message\_seconds? | integer | number of seconds to delete messages for, between 0 and 604800 (7 days) | 0 |
###### Bulk Ban Response
On success, this endpoint returns a 200 success response with the following body.
| Field | Type | Description |
| ------------- | ------------------- | ----------------------------------------------- |
| banned\_users | array of snowflakes | list of user ids, that were successfully banned |
| failed\_users | array of snowflakes | list of user ids, that were not banned |
If none of the users could be banned, an error response code `500000: Failed to ban users` is returned instead.
## Get Guild Roles
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/roles
Returns a list of [role](/developers/topics/permissions#role-object) objects for the guild.
## Get Guild Role
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/roles/[\{role.id}](/developers/topics/permissions#role-object)
Returns a [role](/developers/topics/permissions#role-object) object for the specified role.
## Get Guild Role Member Counts
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/roles/member-counts
Returns a map of role IDs to the number of members with the role. Does not include the @everyone role.
###### Example Response
```json theme={"system"}
{
"613425648685547541": 1337,
"1409696176629878905": 2,
"697138785317814292": 67
}
```
## Create Guild Role
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/roles
Create a new [role](/developers/topics/permissions#role-object) for the guild. Requires the `MANAGE_ROLES` permission. Returns the new [role](/developers/topics/permissions#role-object) object on success. Fires a [Guild Role Create](/developers/events/gateway-events#guild-role-create) Gateway event. All JSON params are optional.
This endpoint supports the `X-Audit-Log-Reason` header.
###### JSON Params
| Field | Type | Description | Default |
| -------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- |
| name | string | name of the role, max 100 characters | "new role" |
| permissions | string | bitwise value of the enabled/disabled permissions | @everyone permissions in guild |
| color\* | integer | **Deprecated** RGB color value | 0 |
| colors | [role colors](/developers/topics/permissions#role-object-role-colors-object) object | the role's colors | [default role colors object](/developers/topics/permissions#role-object-default-role-colors-object) |
| hoist | boolean | whether the role should be displayed separately in the sidebar | false |
| icon | ?[image data](/developers/reference#image-data) | the role's icon image (if the guild has the `ROLE_ICONS` feature) | null |
| unicode\_emoji | ?string | the role's unicode emoji as a [standard emoji](/developers/reference#message-formatting) (if the guild has the `ROLE_ICONS` feature) | null |
| mentionable | boolean | whether the role should be mentionable | false |
\* `color` will still be returned by the API, but using the `colors` field is recommended when doing requests.
## Modify Guild Role Positions
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/roles
Modify the positions of a set of [role](/developers/topics/permissions#role-object) objects for the guild. Requires the `MANAGE_ROLES` permission. Returns a list of all of the guild's [role](/developers/topics/permissions#role-object) objects on success. Fires multiple [Guild Role Update](/developers/events/gateway-events#guild-role-update) Gateway events.
This endpoint supports the `X-Audit-Log-Reason` header.
This endpoint takes a JSON array of parameters in the following format:
###### JSON Params
| Field | Type | Description |
| --------- | --------- | ---------------------------------------------------------------------------- |
| id | snowflake | role |
| position? | ?integer | sorting position of the role (roles with the same position are sorted by id) |
## Modify Guild Role
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/roles/[\{role.id}](/developers/topics/permissions#role-object)
Modify a guild role. Requires the `MANAGE_ROLES` permission. Returns the updated [role](/developers/topics/permissions#role-object) on success. Fires a [Guild Role Update](/developers/events/gateway-events#guild-role-update) Gateway event.
All parameters to this endpoint are optional and nullable.
This endpoint supports the `X-Audit-Log-Reason` header.
###### JSON Params
| Field | Type | Description |
| -------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| name | string | name of the role, max 100 characters |
| permissions | string | bitwise value of the enabled/disabled permissions |
| color\* | integer | **Deprecated** RGB color value |
| colors | [role colors](/developers/topics/permissions#role-object-role-colors-object) object | the role's colors |
| hoist | boolean | whether the role should be displayed separately in the sidebar |
| icon | [image data](/developers/reference#image-data) | the role's icon image (if the guild has the `ROLE_ICONS` feature) |
| unicode\_emoji | string | the role's unicode emoji as a [standard emoji](/developers/reference#message-formatting) (if the guild has the `ROLE_ICONS` feature) |
| mentionable | boolean | whether the role should be mentionable |
\* `color` will still be returned by the API, but using the `colors` field is recommended when doing requests.
## Delete Guild Role
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/roles/[\{role.id}](/developers/topics/permissions#role-object)
Delete a guild role. Requires the `MANAGE_ROLES` permission. Returns a 204 empty response on success. Fires a [Guild Role Delete](/developers/events/gateway-events#guild-role-delete) Gateway event.
This endpoint supports the `X-Audit-Log-Reason` header.
## Get Guild Prune Count
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/prune
Returns an object with one `pruned` key indicating the number of members that would be removed in a prune operation. Requires the `MANAGE_GUILD` and `KICK_MEMBERS` permissions.
By default, prune will not remove users with roles. You can optionally include specific roles in your prune by providing the `include_roles` parameter. Any inactive user that has a subset of the provided role(s) will be counted in the prune and users with additional roles will not.
###### Query String Params
| Field | Type | Description | Default |
| -------------- | ------------------------------------------- | ---------------------------------------- | ------- |
| days | integer | number of days to count prune for (1-30) | 7 |
| include\_roles | string; comma-delimited array of snowflakes | role(s) to include | none |
## Begin Guild Prune
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/prune
Begin a prune operation. Requires the `MANAGE_GUILD` and `KICK_MEMBERS` permissions. Returns an object with one `pruned` key indicating the number of members that were removed in the prune operation. For large guilds it's recommended to set the `compute_prune_count` option to `false`, forcing `pruned` to `null`. Fires multiple [Guild Member Remove](/developers/events/gateway-events#guild-member-remove) Gateway events.
By default, prune will not remove users with roles. You can optionally include specific roles in your prune by providing the `include_roles` parameter. Any inactive user that has a subset of the provided role(s) will be included in the prune and users with additional roles will not.
This endpoint supports the `X-Audit-Log-Reason` header.
###### JSON Params
| Field | Type | Description | Default |
| --------------------- | ------------------- | ---------------------------------------------------------- | ------- |
| days | integer | number of days to prune (1-30) | 7 |
| compute\_prune\_count | boolean | whether `pruned` is returned, discouraged for large guilds | true |
| include\_roles | array of snowflakes | role(s) to include | none |
| reason? | string | reason for the prune (deprecated) | |
## Get Guild Voice Regions
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/regions
Returns a list of [voice region](/developers/resources/voice#voice-region-object) objects for the guild. Unlike the similar `/voice` route, this returns VIP servers when the guild is VIP-enabled.
## Get Guild Invites
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/invites
Returns a list of [invite](/developers/resources/invite#invite-object) objects. Requires the `MANAGE_GUILD` or `VIEW_AUDIT_LOG` permission. [Invite Metadata](/developers/resources/invite#invite-metadata-object) is included with the `MANAGE_GUILD` permission.
## Get Guild Integrations
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/integrations
Returns a list of [integration](/developers/resources/guild#integration-object) objects for the guild. Requires the `MANAGE_GUILD` permission.
This endpoint returns a maximum of 50 integrations. If a guild has more integrations, they cannot be accessed.
## Delete Guild Integration
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/integrations/[\{integration.id}](/developers/resources/guild#integration-object)
Delete the attached [integration](/developers/resources/guild#integration-object) object for the guild. Deletes any associated webhooks and kicks the associated bot if there is one. Requires the `MANAGE_GUILD` permission. Returns a 204 empty response on success. Fires [Guild Integrations Update](/developers/events/gateway-events#guild-integrations-update) and [Integration Delete](/developers/events/gateway-events#integration-delete) Gateway events.
This endpoint supports the `X-Audit-Log-Reason` header.
## Get Guild Widget Settings
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/widget
Returns a [guild widget settings](/developers/resources/guild#guild-widget-settings-object) object. Requires the `MANAGE_GUILD` permission.
## Modify Guild Widget
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/widget
Modify a [guild widget settings](/developers/resources/guild#guild-widget-settings-object) object for the guild. All attributes may be passed in with JSON and modified. Requires the `MANAGE_GUILD` permission. Returns the updated [guild widget settings](/developers/resources/guild#guild-widget-settings-object) object. Fires a [Guild Update](/developers/events/gateway-events#guild-update) Gateway event.
This endpoint supports the `X-Audit-Log-Reason` header.
## Get Guild Widget
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/widget.json
Returns the [widget](/developers/resources/guild#guild-widget-object) for the guild. Fires an [Invite Create](/developers/events/gateway-events#invite-create) Gateway event when an invite channel is defined and a new [Invite](/developers/resources/invite#invite-object) is generated.
## Get Guild Vanity URL
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/vanity-url
Returns a partial [invite](/developers/resources/invite#invite-object) object for guilds with that feature enabled. Requires the `MANAGE_GUILD` permission. `code` will be null if a vanity url for the guild is not set.
This endpoint is required to get the usage count of the vanity invite, but the invite code can be accessed as `vanity_url_code` in the [guild object](/developers/resources/guild#guild-object) without having the `MANAGE_GUILD` permission.
###### Example Partial Invite Object
```json theme={"system"}
{
"code": "abc",
"uses": 12
}
```
## Get Guild Widget Image
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/widget.png
Returns a PNG image widget for the guild. Requires no permissions or authentication.
All parameters to this endpoint are optional.
###### Query String Params
| Field | Type | Description | Default |
| ----- | ------ | ---------------------------------------------- | ------- |
| style | string | style of the widget image returned (see below) | shield |
###### Widget Style Options
| Value | Description | Example |
| ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| shield | shield style widget with Discord icon and guild members online count | [Example](https://discord.com/api/guilds/81384788765712384/widget.png?style=shield) |
| banner1 | large image with guild icon, name and online count. "POWERED BY DISCORD" as the footer of the widget | [Example](https://discord.com/api/guilds/81384788765712384/widget.png?style=banner1) |
| banner2 | smaller widget style with guild icon, name and online count. Split on the right with Discord logo | [Example](https://discord.com/api/guilds/81384788765712384/widget.png?style=banner2) |
| banner3 | large image with guild icon, name and online count. In the footer, Discord logo on the left and "Chat Now" on the right | [Example](https://discord.com/api/guilds/81384788765712384/widget.png?style=banner3) |
| banner4 | large Discord logo at the top of the widget. Guild icon, name and online count in the middle portion of the widget and a "JOIN MY SERVER" button at the bottom | [Example](https://discord.com/api/guilds/81384788765712384/widget.png?style=banner4) |
## Get Guild Welcome Screen
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/welcome-screen
Returns the [Welcome Screen](/developers/resources/guild#welcome-screen-object) object for the guild. If the welcome screen is not enabled, the `MANAGE_GUILD` permission is required.
## Modify Guild Welcome Screen
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/welcome-screen
Modify the guild's [Welcome Screen](/developers/resources/guild#welcome-screen-object). Requires the `MANAGE_GUILD` permission. Returns the updated [Welcome Screen](/developers/resources/guild#welcome-screen-object) object. May fire a [Guild Update](/developers/events/gateway-events#guild-update) Gateway event.
All parameters to this endpoint are optional and nullable.
This endpoint supports the `X-Audit-Log-Reason` header.
###### JSON Params
| Field | Type | Description |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
| enabled | boolean | whether the welcome screen is enabled |
| welcome\_channels | array of [welcome screen channel](/developers/resources/guild#welcome-screen-object-welcome-screen-channel-structure) objects | channels linked in the welcome screen and their display options |
| description | string | the server description to show in the welcome screen |
## Get Guild Onboarding
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/onboarding
Returns the [Onboarding](/developers/resources/guild#guild-onboarding-object) object for the guild.
## Modify Guild Onboarding
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/onboarding
Modifies the onboarding configuration of the guild. Returns a 200 with the [Onboarding](/developers/resources/guild#guild-onboarding-object) object for the guild. Requires the `MANAGE_GUILD` and `MANAGE_ROLES` permissions.
Onboarding enforces constraints when enabled. These constraints are that there must be at least 7 Default Channels and at least 5 of them must allow sending messages to the @everyone role. The `mode` field modifies what is considered when enforcing these constraints.
This endpoint supports the `X-Audit-Log-Reason` header.
All parameters to this endpoint are optional.
###### JSON Params
| Field | Type | Description |
| --------------------- | --------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| prompts | array of [onboarding prompt](/developers/resources/guild#guild-onboarding-object-onboarding-prompt-structure) objects | Prompts shown during onboarding and in customize community |
| default\_channel\_ids | array of snowflakes | Channel IDs that members get opted into automatically |
| enabled | boolean | Whether onboarding is enabled in the guild |
| mode | [onboarding mode](/developers/resources/guild#guild-onboarding-object-onboarding-mode) | Current mode of onboarding |
## Modify Guild Incident Actions
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/incident-actions
Modifies the incident actions of the guild. Returns a 200 with the [Incidents Data](/developers/resources/guild#incidents-data-object) object for the guild. Requires the `MANAGE_GUILD` permission.
###### JSON Params
Both `invites_disabled_until` and `dms_disabled_until` can be enabled for a maximal timespan of 24 hours in the future.
| Field | Type | Description |
| ------------------------- | --------------------- | ------------------------------------------ |
| invites\_disabled\_until? | ?ISO8601 timestamp \* | when invites will be enabled again |
| dms\_disabled\_until? | ?ISO8601 timestamp \* | when direct messages will be enabled again |
\* Supplying `null` disables the action.
# Guild Scheduled Event
Source: https://docs.discord.com/developers/resources/guild-scheduled-event
Reference for Discord scheduled event objects and management endpoints.
A representation of a scheduled event in a [guild](/developers/resources/guild).
### Guild Scheduled Event Object
###### Guild Scheduled Event Structure
| Field | Type | Description |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id | snowflake | the id of the scheduled event |
| guild\_id | snowflake | the guild id which the scheduled event belongs to |
| channel\_id \*\* | ?snowflake | the channel id in which the scheduled event will be hosted, or `null` if [scheduled entity type](/developers/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-entity-types) is `EXTERNAL` |
| creator\_id? \* | ?snowflake | the id of the user that created the scheduled event \* |
| name | string | the name of the scheduled event (1-100 characters) |
| description? | ?string | the description of the scheduled event (1-1000 characters) |
| scheduled\_start\_time | ISO8601 timestamp | the time the scheduled event will start |
| scheduled\_end\_time \*\* | ?ISO8601 timestamp | the time the scheduled event will end, required if entity\_type is `EXTERNAL` |
| privacy\_level | [privacy level](/developers/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-privacy-level) | the privacy level of the scheduled event |
| status | [event status](/developers/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-status) | the status of the scheduled event |
| entity\_type | [scheduled entity type](/developers/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-entity-types) | the type of the scheduled event |
| entity\_id | ?snowflake | the id of an entity associated with a guild scheduled event |
| entity\_metadata \*\* | ?[entity metadata](/developers/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-entity-metadata) | additional metadata for the guild scheduled event |
| creator? | [user](/developers/resources/user#user-object) object | the user that created the scheduled event |
| user\_count? | integer | the number of users subscribed to the scheduled event |
| image? | ?string | the [cover image hash](/developers/reference#image-formatting) of the scheduled event |
| recurrence\_rule | ?[recurrence rule](/developers/resources/guild-scheduled-event#guild-scheduled-event-recurrence-rule-object) | the definition for how often this event should recur |
\* `creator_id` will be null and `creator` will not be included for events created before October 25th, 2021, when the concept of `creator_id` was introduced and tracked.
\*\* See [field requirements by entity type](/developers/resources/guild-scheduled-event#guild-scheduled-event-object-field-requirements-by-entity-type) to understand the relationship between `entity_type` and the following fields: `channel_id`, `entity_metadata`, and `scheduled_end_time`
###### Guild Scheduled Event Privacy Level
| Level | Value | Description |
| ----------- | ----- | ------------------------------------------------------- |
| GUILD\_ONLY | 2 | the scheduled event is only accessible to guild members |
###### Guild Scheduled Event Entity Types
| Type | Value |
| --------------- | ----- |
| STAGE\_INSTANCE | 1 |
| VOICE | 2 |
| EXTERNAL | 3 |
###### Field Requirements By Entity Type
The following table shows field requirements based on current entity type.
`value` : This field is required to be a non-null value
`null` : This field is required to be null
`-` : No strict requirements
| Entity Type | channel\_id | entity\_metadata | scheduled\_end\_time |
| --------------- | ----------- | ---------------- | -------------------- |
| STAGE\_INSTANCE | value | null | - |
| VOICE | value | null | - |
| EXTERNAL | null | value \* | value |
\* `entity_metadata` with a non-null `location` must be provided
###### Guild Scheduled Event Status
| Type | Value |
| ------------ | ----- |
| SCHEDULED | 1 |
| ACTIVE | 2 |
| COMPLETED \* | 3 |
| CANCELED \* | 4 |
\* Once `status` is set to `COMPLETED` or `CANCELED`, the `status` can no longer be updated
###### Valid Guild Scheduled Event Status Transitions
SCHEDULED --> ACTIVE
ACTIVE --------> COMPLETED
SCHEDULED --> CANCELED
###### Guild Scheduled Event Entity Metadata
| Field | Type | Description |
| ------------ | ------ | ---------------------------------------- |
| location? \* | string | location of the event (1-100 characters) |
\* [required](/developers/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-entity-metadata) for events with `'entity_type': EXTERNAL`
### Guild Scheduled Event User Object
###### Guild Scheduled Event User Structure
| Field | Type | Description |
| --------------------------- | --------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| guild\_scheduled\_event\_id | snowflake | the scheduled event id which the user subscribed to |
| user | [user](/developers/resources/user#user-object) | user which subscribed to an event |
| member? | [guild member](/developers/resources/guild#guild-member-object) | guild member data for this user for the guild which this event belongs to, if any |
### Guild Scheduled Event Recurrence Rule Object
Discord's recurrence rule is a subset of the behaviors [defined in the iCalendar RFC](https://datatracker.ietf.org/doc/html/rfc5545) and implemented by [python's dateutil rrule](https://dateutil.readthedocs.io/en/stable/rrule.html)
There are currently many limitations to this system. Please see "System limitations" below
###### Guild Scheduled Event Recurrence Rule Structure
| Field | Type | Description |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| start | ISO8601 timestamp | Starting time of the recurrence interval |
| end \[1] | ?ISO8601 timestamp | Ending time of the recurrence interval |
| frequency | [recurrence rule - frequency](/developers/resources/guild-scheduled-event#guild-scheduled-event-recurrence-rule-object-guild-scheduled-event-recurrence-rule-frequency) object | How often the event occurs |
| interval | int | The spacing between the events, defined by `frequency`. For example, `frequency` of `WEEKLY` and an `interval` of `2` would be "every-other week" |
| by\_weekday | ?array of [recurrence rule - weekday](/developers/resources/guild-scheduled-event#guild-scheduled-event-recurrence-rule-object-guild-scheduled-event-recurrence-rule-weekday) | Set of specific days within a week for the event to recur on |
| by\_n\_weekday | ?array of [recurrence rule - n\_weekday](/developers/resources/guild-scheduled-event#guild-scheduled-event-recurrence-rule-object-guild-scheduled-event-recurrence-rule-nweekday-structure) objects | List of specific days within a specific week (1-5) to recur on |
| by\_month | ?array of [recurrence rule - month](/developers/resources/guild-scheduled-event#guild-scheduled-event-recurrence-rule-object-guild-scheduled-event-recurrence-rule-month) | Set of specific months to recur on |
| by\_month\_day | ?array of int | Set of specific dates within a month to recur on |
| by\_year\_day \[1] | ?array of int | Set of days within a year to recur on (1-364) |
| count \[1] | ?int | The total amount of times that the event is allowed to recur before stopping |
\[1] - Cannot be set externally currently.
The current system limitations are present due to how reoccurring event data needs to be displayed in the client.
In the future, we would like to open the system up to have fewer / none of these restrictions.
###### The following fields cannot be set by the client / application
* `count`
* `end`
* `by_year_day`
###### The following combinations are mutually exclusive
* `by_weekday`
* `by_n_weekday`
* `by_month` + `by_month_day`
###### `by_weekday`
* Only valid for daily and weekly events (`frequency` of `DAILY` (`0`) or `WEEKLY` (`1`))
* when used in a daily event (`frequency` is `DAILY` (`0`))
* The values present in the `by_weekday` event must be a "known set" of weekdays.
* The following are current allowed "sets"
* Monday - Friday (`[0, 1, 2, 3, 4]`)
* Tuesday - Saturday (`[1, 2, 3, 4, 5]`)
* Sunday - Thursday (`[6, 0, 1, 2, 3]`)
* Friday & Saturday (`[4, 5]`)
* Saturday & Sunday (`[5, 6]`)
* Sunday & Monday (`[6, 0]`)
* when used in a weekly event (`frequency` is `WEEKLY` (`1`))
* `by_weekday` array currently can only be a length of `1`
* i.e: You can only select a single day within a week to have a recurring event on
* If you wish to have multiple days within a week have a recurring event, please use a `frequency` of `DAILY`
* Also, see `interval` below for "every-other" week information
###### `by_n_weekday`
* Only valid for monthly events (`frequency` of `MONTHLY` (`1`))
* `by_n_weekday` array currently can only be a length of `1`
* i.e: You can only select a single day within a month to have a recurring event on
###### `by_month` and `by_month_day`
* Only valid for annual event (`frequency` is `YEARLY` (`0`))
* both `by_month` and `by_month_day` must be provided
* both `by_month` and `by_month_day` arrays must have a length of `1`
* (i.e: You can only set a single date for annual events)
###### `interval` can only be set to a value other than `1` when `frequency` is set to `WEEKLY` (`1`)
* In this situation, interval can be set to `2`
* This allowance enables "every-other week" events
* Due to the limitations placed on `by_weekday` this means that if you wish to use "every-other week" functionality
you can only do so for a single day.
**Every weekday**
```js theme={"system"}
frequency = 3 // Frequency.DAILY
interval = 1
by_weekday = [0, 1, 2, 3, 4] // [Weekday.MONDAY, ..., Weekday.FRIDAY]
```
**Every Wednesday**
```js theme={"system"}
frequency = 2 // Frequency.WEEKLY
interval = 1
by_weekday = [2] // [Weekday.WEDNESDAY]
```
**Every other Wednesday**
```js theme={"system"}
frequency = 2 // Frequency.WEEKLY
interval = 2
by_weekday = [2] // [Weekday.WEDNESDAY]
```
**Monthly on the fourth Wednesday**
```js theme={"system"}
frequency = 1 // Frequency.MONTHLY
interval = 1
by_n_weekday = [{
n: 4,
day: 2 // Weekday.WEDNESDAY
}]
```
**Annually on July 24**
```js theme={"system"}
frequency = 0 // Frequency.YEARLY
interval = 1
by_month = [7] // [Month.JULY]
by_month_day = [24]
```
###### Guild Scheduled Event Recurrence Rule - Frequency
| Type | Value |
| ------- | ----- |
| YEARLY | 0 |
| MONTHLY | 1 |
| WEEKLY | 2 |
| DAILY | 3 |
###### Guild Scheduled Event Recurrence Rule - Weekday
| Type | Value |
| --------- | ----- |
| MONDAY | 0 |
| TUESDAY | 1 |
| WEDNESDAY | 2 |
| THURSDAY | 3 |
| FRIDAY | 4 |
| SATURDAY | 5 |
| SUNDAY | 6 |
###### Guild Scheduled Event Recurrence Rule - N\_Weekday Structure
| Field | Type | Description |
| ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------- |
| n | int | The week to reoccur on. 1 - 5 |
| day | [recurrence rule - weekday](/developers/resources/guild-scheduled-event#guild-scheduled-event-recurrence-rule-object-guild-scheduled-event-recurrence-rule-weekday) | The day within the week to reoccur on |
###### Guild Scheduled Event Recurrence Rule - Month
| Type | Value |
| --------- | ----- |
| JANUARY | 1 |
| FEBRUARY | 2 |
| MARCH | 3 |
| APRIL | 4 |
| MAY | 5 |
| JUNE | 6 |
| JULY | 7 |
| AUGUST | 8 |
| SEPTEMBER | 9 |
| OCTOBER | 10 |
| NOVEMBER | 11 |
| DECEMBER | 12 |
## List Scheduled Events for Guild
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/scheduled-events
Returns a list of [guild scheduled event](/developers/resources/guild-scheduled-event#guild-scheduled-event-object) objects for the given guild.
###### Query String Params
| Field | Type | Description |
| ------------------ | ------------------------------------------------------ | ------------------------------------------------ |
| with\_user\_count? | [boolean](/developers/reference#boolean-query-strings) | include number of users subscribed to each event |
## Create Guild Scheduled Event
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/scheduled-events
Create a guild scheduled event in the guild. Returns a [guild scheduled event](/developers/resources/guild-scheduled-event#guild-scheduled-event-object) object on success. Fires a [Guild Scheduled Event Create](/developers/events/gateway-events#guild-scheduled-event-create) Gateway event.
A guild can have a maximum of 100 events with `SCHEDULED` or `ACTIVE` status at any time.
This endpoint supports the `X-Audit-Log-Reason` header.
###### JSON Params
| Field | Type | Description |
| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- |
| channel\_id? \* | snowflake \* | the channel id of the scheduled event. |
| entity\_metadata? \*\* | [entity metadata](/developers/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-entity-metadata) | the entity metadata of the scheduled event |
| name | string | the name of the scheduled event |
| privacy\_level | [privacy level](/developers/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-privacy-level) | the privacy level of the scheduled event |
| scheduled\_start\_time | ISO8601 timestamp | the time to schedule the scheduled event |
| scheduled\_end\_time? \*\* | ISO8601 timestamp | the time when the scheduled event is scheduled to end |
| description? | string | the description of the scheduled event |
| entity\_type | [entity type](/developers/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-entity-types) | the entity type of the scheduled event |
| image? | [image data](/developers/reference#image-data) | the cover image of the scheduled event |
| recurrence\_rule? | [recurrence rule](/developers/resources/guild-scheduled-event#guild-scheduled-event-recurrence-rule-object) | the definition for how often this event should recur |
\* Optional for events with `'entity_type': EXTERNAL`
\*\* Required for events with `'entity_type': EXTERNAL`
## Get Guild Scheduled Event
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/scheduled-events/[\{guild\_scheduled\_event.id}](/developers/resources/guild-scheduled-event#guild-scheduled-event-object)
Get a guild scheduled event. Returns a [guild scheduled event](/developers/resources/guild-scheduled-event#guild-scheduled-event-object) object on success.
###### Query String Params
| Field | Type | Description |
| ------------------ | ------------------------------------------------------ | ------------------------------------------------ |
| with\_user\_count? | [boolean](/developers/reference#boolean-query-strings) | include number of users subscribed to this event |
## Modify Guild Scheduled Event
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/scheduled-events/[\{guild\_scheduled\_event.id}](/developers/resources/guild-scheduled-event#guild-scheduled-event-object)
Modify a guild scheduled event. Returns the modified [guild scheduled event](/developers/resources/guild-scheduled-event#guild-scheduled-event-object) object on success. Fires a [Guild Scheduled Event Update](/developers/events/gateway-events#guild-scheduled-event-update) Gateway event.
To start or end an event, use this endpoint to modify the event's [status](/developers/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-status) field.
This endpoint supports the `X-Audit-Log-Reason` header.
This endpoint silently discards `entity_metadata` for non-`EXTERNAL` events.
All parameters to this endpoint are optional.
###### JSON Params
| Field | Type | Description |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| channel\_id? \* | ?snowflake | the channel id of the scheduled event, set to `null` if changing entity type to `EXTERNAL` |
| entity\_metadata? | ?[entity metadata](/developers/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-entity-metadata) | the entity metadata of the scheduled event |
| name? | string | the name of the scheduled event |
| privacy\_level? | [privacy level](/developers/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-privacy-level) | the privacy level of the scheduled event |
| scheduled\_start\_time? | ISO8601 timestamp | the time to schedule the scheduled event |
| scheduled\_end\_time? \* | ISO8601 timestamp | the time when the scheduled event is scheduled to end |
| description? | ?string | the description of the scheduled event |
| entity\_type? \* | [event entity type](/developers/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-entity-types) | the entity type of the scheduled event |
| status? | [event status](/developers/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-status) | the status of the scheduled event |
| image? | [image data](/developers/reference#image-data) | the cover image of the scheduled event |
| recurrence\_rule? | ?[recurrence rule](/developers/resources/guild-scheduled-event#guild-scheduled-event-recurrence-rule-object) | the definition for how often this event should recur |
\* If updating `entity_type` to `EXTERNAL`:
* `channel_id` is required and [must be set to null](/developers/resources/guild-scheduled-event#guild-scheduled-event-object-field-requirements-by-entity-type)
* `entity_metadata` with a `location` field must be provided
* `scheduled_end_time` must be provided
## Delete Guild Scheduled Event
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/scheduled-events/[\{guild\_scheduled\_event.id}](/developers/resources/guild-scheduled-event#guild-scheduled-event-object)
Delete a guild scheduled event. Returns a `204` on success. Fires a [Guild Scheduled Event Delete](/developers/events/gateway-events#guild-scheduled-event-delete) Gateway event.
## Get Guild Scheduled Event Users
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/scheduled-events/[\{guild\_scheduled\_event.id}](/developers/resources/guild-scheduled-event#guild-scheduled-event-object)/users
Get a list of guild scheduled event users subscribed to a guild scheduled event. Returns a list of [guild scheduled event user](/developers/resources/guild-scheduled-event#guild-scheduled-event-user-object) objects on success. Guild member data, if it exists, is included if the `with_member` query parameter is set.
###### Query String Params
| Field | Type | Description | Default |
| ------------- | ------------------------------------------------------ | --------------------------------------------- | ------- |
| limit? | number | number of users to return (up to maximum 100) | 100 |
| with\_member? | [boolean](/developers/reference#boolean-query-strings) | include guild member data if it exists | false |
| before? \* | snowflake | consider only users before given user id | null |
| after? \* | snowflake | consider only users after given user id | null |
\* Provide a user id to `before` and `after` for pagination. Users will always be returned in ascending order by `user_id`. If both `before` and `after` are provided, only `before` is respected. Fetching users in-between `before` and `after` is not supported.
## Guild Scheduled Event Status Update Automation
NOTE: `status` and `entity_type` here are expressed by name rather than their value for readability
### An active scheduled event for a stage channel where all users have left the stage channel will automatically end a few minutes after the last user leaves the channel
When an event with `'status': ACTIVE` and `'entity_type': STAGE_INSTANCE` has no users connected to the stage channel for a certain period of time (on the order of minutes), the event `status` will be automatically set to `COMPLETED`.
### An active scheduled event for a voice channel where all users have left the voice channel will automatically end a few minutes after the last user leaves the channel
When an event with `'status': ACTIVE` and `'entity_type': VOICE` has no users connected to the voice channel for a certain period of time (on the order of minutes), the event `status` will be automatically set to `COMPLETED`.
### An external event will automatically begin at its scheduled start time
An event with `'entity_type': EXTERNAL` at its `scheduled_start_time` will automatically have `status` set to `ACTIVE`.
### An external event will automatically end at its scheduled end time
An event with `'entity_type': EXTERNAL` at its `scheduled_end_time` will automatically have `status` set to `COMPLETED`.
### Any scheduled event which has not begun after its scheduled start time will be automatically cancelled after a few hours
Any event with `'status': SCHEDULED` after a certain time interval (on the order of hours) beyond its `scheduled_start_time` will have its `status` automatically set to `CANCELLED`.
## Guild Scheduled Event Permissions Requirements
NOTE: `entity_type` is expressed by name rather than value for readability
A user must be a member of the guild in order to access events for that guild unless the guild is public. If a guild is public, events in that guild may be visible depending on the event type and the permissions of any channels associated with the event.
### Permissions for events with entity\_type: STAGE\_INSTANCE
The computed permissions for the user in the `channel_id` associated with the event must include:
#### Get Permissions
* `VIEW_CHANNEL`
#### Create Permissions
* `CREATE_EVENTS`
* `MANAGE_CHANNELS`
* `MUTE_MEMBERS`
* `MOVE_MEMBERS`
#### Modify/Delete Permissions
* If the event was created by the current user, either `CREATE_EVENTS` or `MANAGE_EVENTS`. Otherwise, `MANAGE_EVENTS`
* `MANAGE_CHANNELS`
* `MUTE_MEMBERS`
* `MOVE_MEMBERS`
### Permissions for events with entity\_type: VOICE
The computed permissions for the user in the `channel_id` associated with the event must include:
#### Get Permissions
* `VIEW_CHANNEL`
#### Create permissions
* `CREATE_EVENTS`
* `VIEW_CHANNEL`
* `CONNECT`
#### Modify/Delete Permissions
* If the event was created by the current user, either `CREATE_EVENTS` or `MANAGE_EVENTS`. Otherwise, `MANAGE_EVENTS`
* `VIEW_CHANNEL`
* `CONNECT`
### Permissions for events with entity\_type: EXTERNAL
The user's guild-level permissions must include:
#### Get Permissions
* *No permissions required*
#### Create permissions
* `CREATE_EVENTS`
#### Modify/Delete Permissions
* If the event was created by the current user, either `CREATE_EVENTS` or `MANAGE_EVENTS`. Otherwise, `MANAGE_EVENTS`
# Guild Template Resource
Source: https://docs.discord.com/developers/resources/guild-template
Reference for Discord Guild Template objects and management endpoints.
### Guild Template Object
Represents a code that when used, creates a guild based on a snapshot of an existing guild.
###### Guild Template Structure
| Field | Type | Description |
| ------------------------- | ---------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| code | string | the template code (unique ID) |
| name | string | template name |
| description | ?string | the description for the template |
| usage\_count | integer | number of times this template has been used |
| creator\_id | snowflake | the ID of the user who created the template |
| creator | [user](/developers/resources/user#user-object) object | the user who created the template |
| created\_at | ISO8601 timestamp | when this template was created |
| updated\_at | ISO8601 timestamp | when this template was last synced to the source guild |
| source\_guild\_id | snowflake | the ID of the guild this template is based on |
| serialized\_source\_guild | partial [guild](/developers/resources/guild#guild-object) object | the guild snapshot this template contains; placeholder IDs are given as integers |
| is\_dirty | ?boolean | whether the template has unsynced changes |
###### Example Guild Template Object
```json theme={"system"}
{
"code": "hgM48av5Q69A",
"name": "Friends & Family",
"description": "",
"usage_count": 49605,
"creator_id": "132837293881950208",
"creator": {
"id": "132837293881950208",
"username": "hoges",
"avatar": "79b0d9f8c340f2d43e1f78b09f175b62",
"discriminator": "0001",
"public_flags": 129
},
"created_at": "2020-04-02T21:10:38+00:00",
"updated_at": "2020-05-01T17:57:38+00:00",
"source_guild_id": "678070694164299796",
"serialized_source_guild": {
"name": "Friends & Family",
"description": null,
"region": "us-west",
"verification_level": 0,
"default_message_notifications": 0,
"explicit_content_filter": 0,
"preferred_locale": "en-US",
"afk_timeout": 300,
"roles": [
{
"id": 0,
"name": "@everyone",
"permissions": 104324689,
"color": 0,
"hoist": false,
"mentionable": false
}
],
"channels": [
{
"name": "Text Channels",
"position": 1,
"topic": null,
"bitrate": 64000,
"user_limit": 0,
"nsfw": false,
"rate_limit_per_user": 0,
"parent_id": null,
"permission_overwrites": [],
"id": 1,
"type": 4
},
{
"name": "general",
"position": 1,
"topic": null,
"bitrate": 64000,
"user_limit": 0,
"nsfw": false,
"rate_limit_per_user": 0,
"parent_id": 1,
"permission_overwrites": [],
"id": 2,
"type": 0
}
],
"afk_channel_id": null,
"system_channel_id": 2,
"system_channel_flags": 0,
"icon_hash": null
},
"is_dirty": null
}
```
## Get Guild Template
/guilds/templates/[\{template.code}](/developers/resources/guild-template#guild-template-object)
Returns a [guild template](/developers/resources/guild-template#guild-template-object) object for the given code.
## Get Guild Templates
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/templates
Returns an array of [guild template](/developers/resources/guild-template#guild-template-object) objects. Requires the `MANAGE_GUILD` permission.
## Create Guild Template
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/templates
Creates a template for the guild. Requires the `MANAGE_GUILD` permission. Returns the created [guild template](/developers/resources/guild-template#guild-template-object) object on success.
###### JSON Params
| Field | Type | Description |
| ------------ | ------- | ----------------------------------------------- |
| name | string | name of the template (1-100 characters) |
| description? | ?string | description for the template (0-120 characters) |
## Sync Guild Template
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/templates/[\{template.code}](/developers/resources/guild-template#guild-template-object)
Syncs the template to the guild's current state. Requires the `MANAGE_GUILD` permission. Returns the [guild template](/developers/resources/guild-template#guild-template-object) object on success.
## Modify Guild Template
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/templates/[\{template.code}](/developers/resources/guild-template#guild-template-object)
Modifies the template's metadata. Requires the `MANAGE_GUILD` permission. Returns the [guild template](/developers/resources/guild-template#guild-template-object) object on success.
###### JSON Params
| Field | Type | Description |
| ------------ | ------- | ----------------------------------------------- |
| name? | string | name of the template (1-100 characters) |
| description? | ?string | description for the template (0-120 characters) |
## Delete Guild Template
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/templates/[\{template.code}](/developers/resources/guild-template#guild-template-object)
Deletes the template. Requires the `MANAGE_GUILD` permission. Returns the deleted [guild template](/developers/resources/guild-template#guild-template-object) object on success.
# Invite Resource
Source: https://docs.discord.com/developers/resources/invite
### Invite Object
Represents a code that when used, adds a user to a guild or group DM channel.
###### Invite Structure
| Field | Type | Description |
| ----------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| type | integer | the [type of invite](/developers/resources/invite#invite-object-invite-types) |
| code | string | the invite code (unique ID) |
| guild? | partial [guild](/developers/resources/guild#guild-object) object | the guild this invite is for |
| channel | ?partial [channel](/developers/resources/channel#channel-object) object | the channel this invite is for |
| inviter? | [user](/developers/resources/user#user-object) object | the user who created the invite |
| target\_type? | integer | the [type of target](/developers/resources/invite#invite-object-invite-target-types) for this voice channel invite |
| target\_user? | [user](/developers/resources/user#user-object) object | the user whose stream to display for this voice channel stream invite |
| target\_application? | partial [application](/developers/resources/application#application-object) object | the embedded application to open for this voice channel embedded application invite |
| approximate\_presence\_count? | integer | approximate count of online members, returned from the `GET /invites/` endpoint when `with_counts` is `true` |
| approximate\_member\_count? | integer | approximate count of total members, returned from the `GET /invites/` endpoint when `with_counts` is `true` |
| expires\_at | ?ISO8601 timestamp | the expiration date of this invite |
| guild\_scheduled\_event? | [guild scheduled event](/developers/resources/guild-scheduled-event#guild-scheduled-event-object) object | guild scheduled event data, only included if `guild_scheduled_event_id` contains a valid guild scheduled event id |
| flags? | integer | [guild invite flags](/developers/resources/invite#invite-object-guild-invite-flags) for guild invites |
| roles?\* | array of partial [role](/developers/topics/permissions#role-object) objects | the roles assigned to the user upon accepting the invite. |
\* This is a partial role object that only contains `id`, `name`, `position`, `color`, `colors`, `icon`, and `unicode_emoji`.
###### Invite Types
| Type | Value |
| --------- | ----- |
| GUILD | 0 |
| GROUP\_DM | 1 |
| FRIEND | 2 |
###### Invite Target Types
| Type | Value |
| --------------------- | ----- |
| STREAM | 1 |
| EMBEDDED\_APPLICATION | 2 |
###### Guild Invite Flags
| Flag | Value | Description |
| ----------------- | -------- | ------------------------------------------------- |
| IS\_GUEST\_INVITE | `1 << 0` | this invite is a guest invite for a voice channel |
###### Example Invite Object
```json theme={"system"}
{
"type": 0,
"code": "0vCdhLbwjZZTWZLD",
"guild": {
"id": "165176875973476352",
"name": "CS:GO Fraggers Only",
"splash": null,
"banner": null,
"description": "Very good description",
"icon": null,
"features": ["NEWS", "DISCOVERABLE"],
"verification_level": 2,
"vanity_url_code": null,
"nsfw_level": 0,
"premium_subscription_count": 5
},
"channel": {
"id": "165176875973476352",
"name": "illuminati",
"type": 0
},
"inviter": {
"id": "115590097100865541",
"username": "speed",
"avatar": "deadbeef",
"discriminator": "7653",
"public_flags": 131328
},
"target_type": 1,
"target_user": {
"id": "165176875973476352",
"username": "bob",
"avatar": "deadbeef",
"discriminator": "1234",
"public_flags": 64
}
}
```
### Invite Metadata Object
Extra information about an invite, will extend the [invite](/developers/resources/invite#invite-object) object.
###### Invite Metadata Structure
| Field | Type | Description |
| ----------- | ----------------- | ---------------------------------------------------- |
| uses | integer | number of times this invite has been used |
| max\_uses | integer | max number of times this invite can be used |
| max\_age | integer | duration (in seconds) after which the invite expires |
| temporary | boolean | whether this invite only grants temporary membership |
| created\_at | ISO8601 timestamp | when this invite was created |
###### Example Invite Metadata
```json theme={"system"}
{
"uses": 0,
"max_uses": 0,
"max_age": 0,
"temporary": false,
"created_at": "2016-03-31T19:15:39.954000+00:00"
}
```
### Invite Stage Instance Object
This is deprecated.
###### Invite Stage Instance Structure
| Field | Type | Description |
| ------------------ | ---------------------------------------------------------------------------------------- | -------------------------------------------------- |
| members | array of partial [guild member](/developers/resources/guild#guild-member-object) objects | the members speaking in the Stage |
| participant\_count | integer | the number of users in the Stage |
| speaker\_count | integer | the number of users speaking in the Stage |
| topic | string | the topic of the Stage instance (1-120 characters) |
###### Example Invite Stage Instance
```json theme={"system"}
{
"topic": "The debate is over: diet is better than regular",
"participant_count": 200,
"speaker_count": 5 ,
"members": [
{
"roles": [],
"nick": "NOT API SUPPORT",
"avatar": null,
"premium_since": null,
"joined_at": "2015-04-26T06:26:56.936000+00:00",
"pending": false,
"user": {}
}
]
}
```
## Get Invite
/invites/[\{invite.code}](/developers/resources/invite#invite-object)
Returns an [invite](/developers/resources/invite#invite-object) object for the given code.
###### Query String Params
| Field | Type | Description |
| ---------------------------- | ------------------------------------------------------ | ----------------------------------------------------------- |
| with\_counts? | [boolean](/developers/reference#boolean-query-strings) | whether the invite should contain approximate member counts |
| guild\_scheduled\_event\_id? | snowflake | the guild scheduled event to include with the invite |
## Delete Invite
/invites/[\{invite.code}](/developers/resources/invite#invite-object)
Delete an invite. Requires the `MANAGE_CHANNELS` permission on the channel this invite belongs to, or `MANAGE_GUILD` to remove any invite across the guild. Returns an [invite](/developers/resources/invite#invite-object) object on success. Fires an [Invite Delete](/developers/events/gateway-events#invite-delete) Gateway event.
This endpoint supports the `X-Audit-Log-Reason` header.
## Get Target Users
/invites/[\{invite.code}](/developers/resources/invite#invite-object)/target-users
Gets the users allowed to see and accept this invite. Response is a CSV file with the header `user_id` and each user ID from the original file passed to [invite create](/developers/resources/channel#create-channel-invite) on its own line. Requires the caller to be the inviter, or have `MANAGE_GUILD` permission, or have `VIEW_AUDIT_LOG` permission.
## Update Target Users
/invites/[\{invite.code}](/developers/resources/invite#invite-object)/target-users
Updates the users allowed to see and accept this invite. Uploading a file with invalid user IDs will result in a 400 with the invalid IDs described. Requires the caller to be the inviter or have the `MANAGE_GUILD` permission.
###### Form Params
| Field | Type | Description |
| ------------------- | ---- | ---------------------------------------------------------------------------------------- |
| target\_users\_file | file | a csv file with a single column of user IDs for all the users able to accept this invite |
###### Example Error Response
```json theme={"system"}
{
"target_users_file": [
"Line 5: invalid user ID - must be a valid Discord snowflake",
"Line 6: invalid user ID format"
]
}
```
## Get Target Users Job Status
/invites/[\{invite.code}](/developers/resources/invite#invite-object)/target-users/job-status
Processing target users from a CSV when creating or updating an invite is done asynchronously. This endpoint allows you to check the status of that job. Requires the caller to be the inviter, or have `MANAGE_GUILD` permission, or have `VIEW_AUDIT_LOG` permission.
###### Example Response
```json theme={"system"}
{
"status": 3,
"total_users": 100,
"processed_users": 41,
"created_at": "2025-01-08T12:00:00.000000+00:00",
"completed_at": null,
"error_message": "Failed to parse CSV file"
}
```
###### Status Codes
| Code | Name | Description |
| ---- | ----------- | --------------------------------------------------------------- |
| 0 | UNSPECIFIED | The default value. |
| 1 | PROCESSING | The job is still being processed. |
| 2 | COMPLETED | The job has been completed successfully. |
| 3 | FAILED | The job has failed, see `error_message` field for more details. |
# Lobby Resource
Source: https://docs.discord.com/developers/resources/lobby
Reference for Discord lobby objects used for matchmaking.
### Lobby Object
Represents a lobby within Discord. See [Managing Lobbies](/developers/discord-social-sdk/development-guides/managing-lobbies) for more information.
###### Lobby Structure
| Field | Type | Description |
| ---------------- | -------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| id | snowflake | the id of this channel |
| application\_id | snowflake | application that created the lobby |
| metadata | ?dict\ | dictionary of string key/value pairs. The max total length is 1000. |
| members | array of [lobby member](/developers/resources/lobby#lobby-member-object) objects | members of the lobby |
| linked\_channel? | channel object | the guild channel linked to the lobby |
### Lobby Member Object
Represents a member of a lobby, including optional metadata and flags.
###### Lobby Member Structure
| Field | Type | Description |
| --------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id | snowflake | the id of the user |
| metadata? | ?dict\ | dictionary of string key/value pairs. The max total length is 1000. |
| flags? | integer | [lobby member flags](/developers/resources/lobby#lobby-member-object-lobby-member-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) |
###### Lobby Member Flags
| Flag | Value | Description |
| ------------ | ------ | --------------------------------------- |
| CanLinkLobby | `1<<0` | user can link a text channel to a lobby |
###### Example Lobby Object
```json theme={"system"}
{
"id": "96008815106887111",
"application_id": "41771983429993937",
"metadata": {
"topic": "we need more redstone",
},
"members": [
{
"id": "41771983429993000",
"metadata": null,
"flags": 1
}
]
}
```
## Create Lobby
/lobbies
Creates a new lobby, adding any of the specified members to it, if provided.
Returns a [lobby](/developers/resources/lobby#lobby-object) object.
[Discord Social SDK](/developers/discord-social-sdk/overview) clients will not be able to join or leave a lobby
created using this API, such as [`Client::CreateOrJoinLobby`]. See
[Managing Lobbies](/developers/discord-social-sdk/development-guides/managing-lobbies#server-side-lobby-management)
for more information.
### JSON Params
| Field | Type | Description |
| ----------------------- | -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| metadata? | ?dict\ | optional dictionary of string key/value pairs. The max total length is 1000. |
| members? | array of [lobby member](/developers/resources/lobby#lobby-member-object) objects | optional array of up to 25 users to be added to the lobby |
| idle\_timeout\_seconds? | integer | seconds to wait before shutting down a lobby after it becomes idle. Value can be between 5 and 604800 (7 days). See [`LobbyHandle`] for more details on this behavior. |
#### Lobby Member JSON Params
| Field | Type | Description |
| --------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id | snowflake | Discord user id of the user to add to the lobby |
| metadata? | ?dict\ | optional dictionary of string key/value pairs. The max total length is 1000. |
| flags? | integer | [lobby member flags](/developers/resources/lobby#lobby-member-object-lobby-member-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) |
## Create or Join Lobby
/lobbies
Creates a new lobby for the application identified by a `secret`, or joins the calling user to the existing lobby with that secret if one already exists. Updates lobby metadata and the calling member's metadata on join.
Uses `Bearer` token for authorization with the [`sdk.social_layer`](/developers/discord-social-sdk/core-concepts/oauth2-scopes#default-communication-scopes) scope.
Returns a [lobby](/developers/resources/lobby#lobby-object) object.
### JSON Params
| Field | Type | Description |
| ----------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| secret | string | secret used to identify the lobby. If a lobby for this application already exists with this secret, the caller joins it; otherwise a new lobby is created. Max 250 characters. |
| idle\_timeout\_seconds? | integer | seconds to wait before shutting down a lobby after it becomes idle. Value can be between 5 and 604800 (7 days). See [`LobbyHandle`] for more details on this behavior. |
| lobby\_metadata? | ?dict\ | optional dictionary of string key/value pairs to set on the lobby. The max total length is 1000. Overwrites any existing lobby metadata. |
| member\_metadata? | ?dict\ | optional dictionary of string key/value pairs to set on the calling user's lobby member. The max total length is 1000. |
## Get Lobby
/lobbies/[\{lobby.id}](/developers/resources/lobby#lobby-object)
Returns a [lobby](/developers/resources/lobby#lobby-object) object for the specified lobby id, if it exists.
## Modify Lobby
/lobbies/[\{lobby.id}](/developers/resources/lobby#lobby-object)
Modifies the specified lobby with new values, if provided.
Returns the updated [lobby](/developers/resources/lobby#lobby-object) object.
### JSON Params
| Field | Type | Description |
| ----------------------- | -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| metadata? | ?dict\ | optional dictionary of string key/value pairs. The max total length is 1000. Overwrites any existing metadata. |
| members? | array of [lobby member](/developers/resources/lobby#lobby-member-object) objects | optional array of up to 25 users to replace the lobby members with. If provided, lobby members not in this list will be removed from the lobby. |
| idle\_timeout\_seconds? | integer | seconds to wait before shutting down a lobby after it becomes idle. Value can be between 5 and 604800 (7 days). See [`LobbyHandle`] for more details on this behavior. |
## Delete Lobby
/lobbies/[\{lobby.id}](/developers/resources/lobby#lobby-object)
Deletes the specified lobby if it exists.
It is safe to call even if the lobby is already deleted as well.
Returns nothing.
## Add a Member to a Lobby
/lobbies/[\{lobby.id}](/developers/resources/lobby#lobby-object)/members/[\{user.id}](/developers/resources/user#user-object)
Adds the provided user to the specified lobby. If called when the user is already a member of the lobby will update fields such as metadata on that user instead.
Returns the [lobby member](/developers/resources/lobby#lobby-member-object) object.
### JSON Params
| Field | Type | Description |
| --------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| metadata? | ?dict\ | optional dictionary of string key/value pairs. The max total length is 1000. |
| flags? | integer | [lobby member flags](/developers/resources/lobby#lobby-member-object-lobby-member-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) |
## Bulk Update Lobby Members
/lobbies/[\{lobby.id}](/developers/resources/lobby#lobby-object)/members/bulk
Adds, updates, or removes up to 25 members from the specified lobby in a single request. Members with `remove_member: false` (the default) are upserted — added if not present, or updated with the provided metadata and flags if already a member. Members with `remove_member: true` are removed.
Returns an array of [lobby member](/developers/resources/lobby#lobby-member-object) objects for the upserted members. Removed members are not included in the response.
Users unknown to Discord will return a 404 UNKNOWN\_USER error. Users that fail permission checks or who have already reached the maximum number of lobbies per application (and are not already a member of this lobby) are silently dropped from the upsert set.
### JSON Params
An array of member objects. Minimum 1, maximum 25.
| Field | Type | Description |
| --------------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id | snowflake | Discord user id of the user to add, update, or remove |
| metadata? | ?dict\ | optional dictionary of string key/value pairs. The max total length is 1000. |
| flags? | integer | [lobby member flags](/developers/resources/lobby#lobby-member-object-lobby-member-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) |
| remove\_member? | boolean | if `true`, the user is removed from the lobby instead of upserted. Default `false`. |
## Remove a Member from a Lobby
/lobbies/[\{lobby.id}](/developers/resources/lobby#lobby-object)/members/[\{user.id}](/developers/resources/user#user-object)
Removes the provided user from the specified lobby. It is safe to call this even if the user is no longer a member of the lobby, but will fail if the lobby does not exist.
Returns nothing.
## Leave Lobby
/lobbies/[\{lobby.id}](/developers/resources/lobby#lobby-object)/members/@me
Removes the current user from the specified lobby. It is safe to call this even if the user is no longer a member of the lobby, but will fail if the lobby does not exist.
Uses `Bearer` token for authorization.
Returns nothing.
## Link Channel to Lobby
/lobbies/[\{lobby.id}](/developers/resources/lobby#lobby-object)/channel-linking
Links an existing text channel to a lobby. See [Linked Channels](/developers/discord-social-sdk/development-guides/linked-channels) for more information.
Uses `Bearer` token for authorization and user must be a lobby member with `CanLinkLobby` [lobby member flag](/developers/resources/lobby#lobby-member-object-lobby-member-flags).
Returns a [lobby](/developers/resources/lobby#lobby-object) object with a linked channel.
### JSON Params
| Field | Type | Description |
| ------------ | --------- | ---------------------------------------------------------------------------------------------------------------------- |
| channel\_id? | snowflake | the id of the channel to link to the lobby. If not provided, will unlink any currently linked channels from the lobby. |
## Unlink Channel from Lobby
/lobbies/[\{lobby.id}](/developers/resources/lobby#lobby-object)/channel-linking
Unlinks any currently linked channels from the specified lobby.
Send a request to this endpoint with an empty body to unlink any currently linked channels from the specified lobby.
Uses `Bearer` token for authorization and user must be a lobby member with `CanLinkLobby` [lobby member flag](/developers/resources/lobby#lobby-member-object-lobby-member-flags).
Returns a [lobby](/developers/resources/lobby#lobby-object) object without a linked channel.
## Send Lobby Message
/lobbies/[\{lobby.id}](/developers/resources/lobby#lobby-object)/messages
Sends a message to the specified lobby. The calling user must be a member of the lobby.
Uses `Bearer` token for authorization with the [`sdk.social_layer`](/developers/discord-social-sdk/core-concepts/oauth2-scopes#default-communication-scopes) scope.
Returns the created lobby message object.
If the lobby has a [linked channel](/developers/discord-social-sdk/development-guides/linked-channels), the message is also forwarded to that channel. If forwarding fails (for example, due to AutoMod), the lobby message is still delivered to other lobby members.
### JSON Params
| Field | Type | Description |
| --------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| content | string | message content. Must be non-empty. |
| metadata? | ?dict\ | optional dictionary of string key/value pairs delivered alongside the message to active clients via the Social SDK. Not persisted on the linked channel message. |
| flags? | integer | optional message flags combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field). Only flags creatable by the Social SDK are accepted. |
### Lobby Message Object
| Field | Type | Description |
| --------------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| id | snowflake | id of the message |
| type | integer | [message type](/developers/resources/message#message-object-message-types) |
| content | string | message content |
| lobby\_id | snowflake | id of the lobby this message was sent to |
| channel\_id | snowflake | included for compatibility with the messages interface; equal to `lobby_id` |
| author | user object | the [user](/developers/resources/user#user-object) who sent the message |
| metadata? | ?dict\ | dispatch-only metadata sent with the message |
| moderation\_metadata? | ?dict\ | moderation metadata set via [Update Lobby Message Moderation Metadata](/developers/resources/lobby#update-lobby-message-moderation-metadata) |
| flags | integer | [message flags](/developers/resources/message#message-object-message-flags) bitfield |
| application\_id | snowflake | the application that sent the message |
## Get Lobby Messages
/lobbies/[\{lobby.id}](/developers/resources/lobby#lobby-object)/messages
Returns the most recent messages in the specified lobby. The calling user must be a member of the lobby.
Uses `Bearer` token for authorization with the [`sdk.social_layer`](/developers/discord-social-sdk/core-concepts/oauth2-scopes#default-communication-scopes) scope.
Returns an array of lobby message objects (see [Send Lobby Message](/developers/resources/lobby#send-lobby-message) for the object shape).
### Query Params
| Field | Type | Description |
| ------ | ------- | --------------------------------------------------------- |
| limit? | integer | max number of messages to return (1-200). Defaults to 50. |
## Update Lobby Message Moderation Metadata
/lobbies/[\{lobby.id}](/developers/resources/lobby#lobby-object)/messages/\{message.id}/moderation-metadata
Sets the moderation metadata for a lobby message. The metadata is app-scoped and delivered to active
game clients via the Social SDK as a realtime message update. See [Integrate Moderation](/developers/discord-social-sdk/how-to/integrate-moderation#server-side-chat-moderation)
for the full moderation flow.
Uses `Bot` token for authorization.
Returns `HTTP 204: No Content` on success.
### JSON Params
| Field | Type | Description |
| ----- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
| \* | string | Free-form key–value pairs describing the moderation decision. Up to 5 keys; key length ≤ 1024 characters; value length ≤ 2000 characters. |
## Create Lobby Channel Invite for Self
/lobbies/[\{lobby.id}](/developers/resources/lobby#lobby-object)/members/@me/invites
Creates a single-use guild invite to the lobby's [linked channel](/developers/discord-social-sdk/development-guides/linked-channels), targeted at the calling user. The lobby must have a linked channel and the caller must be a member of the lobby. The invite expires after one hour.
Uses `Bearer` token for authorization with the [`sdk.social_layer`](/developers/discord-social-sdk/core-concepts/oauth2-scopes#default-communication-scopes) scope.
Returns a [lobby invite object](/developers/resources/lobby#lobby-invite-object).
## Create Lobby Channel Invite for User
/lobbies/[\{lobby.id}](/developers/resources/lobby#lobby-object)/members/[\{user.id}](/developers/resources/user#user-object)/invites
Creates a single-use guild invite to the lobby's [linked channel](/developers/discord-social-sdk/development-guides/linked-channels) on behalf of an application, targeted at the specified user. The lobby must have a linked channel. The invite expires after one hour.
Uses `Bot` token for authorization.
Returns a [lobby invite object](/developers/resources/lobby#lobby-invite-object).
###### Lobby Invite Object
| Field | Type | Description |
| ----- | ------ | -------------------------------------------------------------------------------------------- |
| code | string | the [invite](/developers/resources/invite#invite-object) code for the lobby's linked channel |
## Development Rate Limits
Applications without [increased rate limits for production releases](/developers/discord-social-sdk/core-concepts/communication-features#applying-for-increased-rate-limits-for-production-releases) are subject to the following application-wide development limits:
| Endpoint | Limit |
| ---------------------------------------------------- | --------------- |
| `POST /lobbies` | 100 per 2 hours |
| `PUT /lobbies` | 100 per 2 hours |
| `PATCH /lobbies/{lobby.id}` | 100 per 2 hours |
| `PUT /lobbies/{lobby.id}/members/{user.id}` | 100 per 2 hours |
| `DELETE /lobbies/{lobby.id}/members/{user.id}` | 100 per 2 hours |
| `POST /lobbies/{lobby.id}/members/bulk` | 100 per 2 hours |
| `POST /lobbies/{lobby.id}/members/@me/invites` | 100 per 2 hours |
| `POST /lobbies/{lobby.id}/members/{user.id}/invites` | 100 per 2 hours |
| `PATCH /lobbies/{lobby.id}/channel-linking` | 20 per 2 hours |
| `POST /lobbies/{lobby.id}/messages` | 100 per 2 hours |
These are per-application rate limits, not per-user.
These limitations are designed to provide sufficient capacity for development, testing, and small-scale demos while ensuring system stability.
[`Client::CreateOrJoinLobby`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a8b4e195555ecaa89ccdfc0acd28d3512
[`LobbyHandle`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LobbyHandle.html#a04cebab69ab0e7fb930346a14a87e843
# Message Resource
Source: https://docs.discord.com/developers/resources/message
Reference for Discord message objects and management endpoints.
### Message Object
Represents a message sent in a channel within Discord.
###### Message Structure
Fields specific to the `MESSAGE_CREATE` and `MESSAGE_UPDATE` events are listed in the [Gateway documentation](/developers/events/gateway-events#message-create).
An app will receive empty values in the `content`, `embeds`, `attachments`, and `components` fields while `poll` will be omitted if they have not configured (or been approved for) the [`MESSAGE_CONTENT` privileged intent (`1 << 15`)](/developers/events/gateway#message-content-intent).
| Field | Type | Description |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id | snowflake | id of the message |
| channel\_id | snowflake | id of the channel the message was sent in |
| author \[1] | [user](/developers/resources/user#user-object) object | the author of this message (not guaranteed to be a valid user, see below) |
| content \[2] | string | contents of the message |
| timestamp | ISO8601 timestamp | when this message was sent |
| edited\_timestamp | ?ISO8601 timestamp | when this message was edited (or null if never) |
| tts | boolean | whether this was a TTS message |
| mention\_everyone | boolean | whether this message mentions everyone |
| mentions | array of [user](/developers/resources/user#user-object) objects | users specifically mentioned in the message |
| mention\_roles | array of [role](/developers/topics/permissions#role-object) object ids | roles specifically mentioned in this message |
| mention\_channels? \[3] | array of [channel mention](/developers/resources/message#channel-mention-object) objects | channels specifically mentioned in this message |
| attachments \[2] | array of [attachment](/developers/resources/message#attachment-object) objects | any attached files that are not referenced in embeds or components |
| embeds \[2] | array of [embed](/developers/resources/message#embed-object) objects | any embedded content |
| reactions? | array of [reaction](/developers/resources/message#reaction-object) objects | reactions to the message |
| nonce? | integer or string | used for validating a message was sent |
| pinned | boolean | whether this message is pinned |
| webhook\_id? | snowflake | if the message is generated by a webhook, this is the webhook's id |
| type | integer | [type of message](/developers/resources/message#message-object-message-types) |
| activity? | [message activity](/developers/resources/message#message-object-message-activity-structure) object | sent with Rich Presence-related chat embeds |
| application? | partial [application](/developers/resources/application#application-object) object | sent with Rich Presence-related chat embeds |
| application\_id? | snowflake | if the message is an [Interaction](/developers/interactions/receiving-and-responding) or application-owned webhook, this is the id of the application |
| flags? | integer | [message flags](/developers/resources/message#message-object-message-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) |
| message\_reference? | [message reference](/developers/resources/message#message-reference-structure) object | data showing the source of a crosspost, channel follow add, pin, or reply message |
| message\_snapshots? \[5] | array of [message snapshot](/developers/resources/message#message-snapshot-object) objects | the message associated with the `message_reference`. This is a minimal subset of fields in a message (e.g. `author` is excluded.) |
| referenced\_message? \[4] | ?[message object](/developers/resources/message#message-object) | the message associated with the message\_reference |
| interaction\_metadata? | [message interaction metadata object](/developers/resources/message#message-interaction-metadata-object) | Sent if the message is sent as a result of an [interaction](/developers/interactions/receiving-and-responding) |
| interaction? | [message interaction object](/developers/interactions/receiving-and-responding#message-interaction-object-message-interaction-structure) | **Deprecated in favor of `interaction_metadata`**; sent if the message is a response to an [interaction](/developers/interactions/receiving-and-responding) |
| thread? | [channel](/developers/resources/channel#channel-object) object | the thread that was started from this message, includes [thread member](/developers/resources/channel#thread-member-object) object |
| components? \[2] | array of [message components](/developers/components/reference#component-object) | sent if the message contains components like buttons, action rows, or other interactive components |
| sticker\_items? | array of [message sticker item objects](/developers/resources/sticker#sticker-item-object) | sent if the message contains stickers |
| stickers? | array of [sticker](/developers/resources/sticker#sticker-object) objects | **Deprecated** the stickers sent with the message |
| position? | integer | A generally increasing integer (there may be gaps or duplicates) that represents the approximate position of the message in a thread, it can be used to estimate the relative position of the message in a thread in company with `total_message_sent` on parent thread |
| role\_subscription\_data? | [role subscription data](/developers/resources/message#role-subscription-data-object) object | data of the role subscription purchase or renewal that prompted this ROLE\_SUBSCRIPTION\_PURCHASE message |
| resolved? | [resolved](/developers/interactions/receiving-and-responding#interaction-object-resolved-data-structure) data | data for users, members, channels, and roles referenced in this message |
| poll? \[2] | [poll](/developers/resources/poll#poll-object) object | A poll! |
| call? | [message call](/developers/resources/message#message-call-object) object | the call associated with the message |
| shared\_client\_theme? | [shared client theme](/developers/resources/message#shared-client-theme-object) object | the custom client-side theme shared via the message |
\[1] The author object follows the structure of the user object, but is only a valid user in the case where the message is generated by a user or bot user. If the message is generated by a webhook, the author object corresponds to the webhook's id, username, and avatar. You can tell if a message is generated by a webhook by checking for the `webhook_id` on the message object.
\[2] An app will receive empty values in the `content`, `embeds`, `attachments`, and `components` fields while `poll` will be omitted if they have not configured (or been approved for) the [`MESSAGE_CONTENT` privileged intent (`1 << 15`)](/developers/events/gateway#message-content-intent).
\[3] Not all channel mentions in a message will appear in `mention_channels`. Only textual channels that are visible to everyone in a public guild will ever be included. Only crossposted messages (via Channel Following) currently include `mention_channels` at all. If no mentions in the message meet these requirements, this field will not be sent.
\[4] This field is only returned for messages with a `type` of `19` (REPLY), `21` (THREAD\_STARTER\_MESSAGE), or `23` (CONTEXT\_MENU\_COMMAND). If the message is one of these but the `referenced_message` field is not present, the backend did not attempt to fetch the message that was being replied to, so its state is unknown. If the field exists but is null, the referenced message was deleted.
\[5] See [message reference types](/developers/resources/message#message-reference-types)
###### Message Types
Type `19` and `20` are only available in API v8 and above. In v6, they are represented as type `0`. Additionally, type `21` is only available in API v9 and above.
| Type | Value | Deletable |
| ------------------------------------------------- | ----- | --------- |
| DEFAULT | 0 | true |
| RECIPIENT\_ADD | 1 | false |
| RECIPIENT\_REMOVE | 2 | false |
| CALL | 3 | false |
| CHANNEL\_NAME\_CHANGE | 4 | false |
| CHANNEL\_ICON\_CHANGE | 5 | false |
| CHANNEL\_PINNED\_MESSAGE | 6 | true |
| USER\_JOIN | 7 | true |
| GUILD\_BOOST | 8 | true |
| GUILD\_BOOST\_TIER\_1 | 9 | true |
| GUILD\_BOOST\_TIER\_2 | 10 | true |
| GUILD\_BOOST\_TIER\_3 | 11 | true |
| CHANNEL\_FOLLOW\_ADD | 12 | true |
| GUILD\_DISCOVERY\_DISQUALIFIED | 14 | true |
| GUILD\_DISCOVERY\_REQUALIFIED | 15 | true |
| GUILD\_DISCOVERY\_GRACE\_PERIOD\_INITIAL\_WARNING | 16 | true |
| GUILD\_DISCOVERY\_GRACE\_PERIOD\_FINAL\_WARNING | 17 | true |
| THREAD\_CREATED | 18 | true |
| REPLY | 19 | true |
| CHAT\_INPUT\_COMMAND | 20 | true |
| THREAD\_STARTER\_MESSAGE | 21 | false |
| GUILD\_INVITE\_REMINDER | 22 | true |
| CONTEXT\_MENU\_COMMAND | 23 | true |
| AUTO\_MODERATION\_ACTION | 24 | true\* |
| ROLE\_SUBSCRIPTION\_PURCHASE | 25 | true |
| INTERACTION\_PREMIUM\_UPSELL | 26 | true |
| STAGE\_START | 27 | true |
| STAGE\_END | 28 | true |
| STAGE\_SPEAKER | 29 | true |
| STAGE\_TOPIC | 31 | true |
| GUILD\_APPLICATION\_PREMIUM\_SUBSCRIPTION | 32 | true |
| GUILD\_INCIDENT\_ALERT\_MODE\_ENABLED | 36 | true |
| GUILD\_INCIDENT\_ALERT\_MODE\_DISABLED | 37 | true |
| GUILD\_INCIDENT\_REPORT\_RAID | 38 | true |
| GUILD\_INCIDENT\_REPORT\_FALSE\_ALARM | 39 | true |
| PURCHASE\_NOTIFICATION | 44 | true |
| POLL\_RESULT | 46 | true |
\* Can only be deleted by members with `MANAGE_MESSAGES` permission
###### Message Activity Structure
| Field | Type | Description |
| ---------- | ------- | ----------------------------------------------------------------------------------------------- |
| type | integer | [type of message activity](/developers/resources/message#message-object-message-activity-types) |
| party\_id? | string | party\_id from a Rich Presence event |
###### Message Activity Types
| Type | Value |
| ------------- | ----- |
| JOIN | 1 |
| SPECTATE | 2 |
| LISTEN | 3 |
| JOIN\_REQUEST | 5 |
###### Message Flags
| Flag | Value | Description |
| -------------------------------------------- | --------- | --------------------------------------------------------------------------------------- |
| CROSSPOSTED | `1 << 0` | this message has been published to subscribed channels (via Channel Following) |
| IS\_CROSSPOST | `1 << 1` | this message originated from a message in another channel (via Channel Following) |
| SUPPRESS\_EMBEDS | `1 << 2` | do not include any embeds when serializing this message |
| SOURCE\_MESSAGE\_DELETED | `1 << 3` | the source message for this crosspost has been deleted (via Channel Following) |
| URGENT | `1 << 4` | this message came from the urgent message system |
| HAS\_THREAD | `1 << 5` | this message has an associated thread, with the same id as the message |
| EPHEMERAL | `1 << 6` | this message is only visible to the user who invoked the Interaction |
| LOADING | `1 << 7` | this message is an Interaction Response and the bot is "thinking" |
| FAILED\_TO\_MENTION\_SOME\_ROLES\_IN\_THREAD | `1 << 8` | this message failed to mention some roles and add their members to the thread |
| SUPPRESS\_NOTIFICATIONS | `1 << 12` | this message will not trigger push and desktop notifications |
| IS\_VOICE\_MESSAGE | `1 << 13` | this message is a voice message |
| HAS\_SNAPSHOT | `1 << 14` | this message has a snapshot (via Message Forwarding) |
| IS\_COMPONENTS\_V2 \* | `1 << 15` | allows you to create fully [component](/developers/components/overview)-driven messages |
\* Once a message has been sent with this flag, it can't be removed from that message.
###### Example Message
```json theme={"system"}
{
"reactions": [
{
"count": 1,
"count_details": {
"burst": 0,
"normal": 1
},
"me": false,
"me_burst": false,
"emoji": {
"id": null,
"name": "🔥"
},
"burst_colors": []
}
],
"attachments": [],
"tts": false,
"embeds": [],
"timestamp": "2017-07-11T17:27:07.299000+00:00",
"mention_everyone": false,
"id": "334385199974967042",
"pinned": false,
"edited_timestamp": null,
"author": {
"username": "Mason",
"discriminator": "9999",
"id": "53908099506183680",
"avatar": "a_bab14f271d565501444b2ca3be944b25"
},
"mention_roles": [],
"content": "Supa Hot",
"channel_id": "290926798999357250",
"mentions": [],
"type": 0
}
```
###### Example Crossposted Message
```json theme={"system"}
{
"reactions": [
{
"count": 1,
"count_details": {
"burst": 0,
"normal": 1
},
"me": false,
"me_burst": false,
"emoji": {
"id": null,
"name": "🔥"
},
"burst_colors": []
}
],
"attachments": [],
"tts": false,
"embeds": [],
"timestamp": "2017-07-11T17:27:07.299000+00:00",
"mention_everyone": false,
"id": "334385199974967042",
"pinned": false,
"edited_timestamp": null,
"author": {
"username": "Mason",
"discriminator": "9999",
"id": "53908099506183680",
"avatar": "a_bab14f271d565501444b2ca3be944b25"
},
"mention_roles": [],
"mention_channels": [
{
"id": "278325129692446722",
"guild_id": "278325129692446720",
"name": "big-news",
"type": 5
}
],
"content": "Big news! In this <#278325129692446722> channel!",
"channel_id": "290926798999357250",
"mentions": [],
"type": 0,
"flags": 2,
"message_reference": {
"type": 0,
"channel_id": "278325129692446722",
"guild_id": "278325129692446720",
"message_id": "306588351130107906"
}
}
```
### Message Interaction Metadata Object
Metadata about the interaction, including the source of the interaction and relevant server and user IDs.
One of [Application Command Interaction Metadata](/developers/resources/message#message-interaction-metadata-object-application-command-interaction-metadata-structure), [Message Component Interaction Metadata](/developers/resources/message#message-interaction-metadata-object-message-component-interaction-metadata-structure), or [Modal Submit Interaction Metadata](/developers/resources/message#message-interaction-metadata-object-modal-submit-interaction-metadata-structure).
###### Application Command Interaction Metadata Structure
| Field | Type | Description |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id | snowflake | ID of the interaction |
| type | [interaction type](/developers/interactions/receiving-and-responding#interaction-object-interaction-type) | Type of interaction |
| user | [user](/developers/resources/user#user-object) object | User who triggered the interaction |
| authorizing\_integration\_owners | dictionary with keys of [application integration types](/developers/resources/application#application-object-application-integration-types) | IDs for installation context(s) related to an interaction. Details in [Authorizing Integration Owners Object](/developers/interactions/receiving-and-responding#interaction-object-authorizing-integration-owners-object) |
| original\_response\_message\_id? | snowflake | ID of the original response message, present only on [follow-up messages](/developers/interactions/receiving-and-responding) |
| target\_user? | [user](/developers/resources/user#user-object) object | The user the command was run on, present only on [user command](/developers/interactions/application-commands#user-commands) interactions |
| target\_message\_id? | snowflake | The ID of the message the command was run on, present only on [message command](/developers/interactions/application-commands#message-commands) interactions. The original response message will also have `message_reference` and `referenced_message` pointing to this message. |
###### Message Component Interaction Metadata Structure
| Field | Type | Description |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id | snowflake | ID of the interaction |
| type | [interaction type](/developers/interactions/receiving-and-responding#interaction-object-interaction-type) | Type of interaction |
| user | [user](/developers/resources/user#user-object) object | User who triggered the interaction |
| authorizing\_integration\_owners | dictionary with keys of [application integration types](/developers/resources/application#application-object-application-integration-types) | IDs for installation context(s) related to an interaction. Details in [Authorizing Integration Owners Object](/developers/interactions/receiving-and-responding#interaction-object-authorizing-integration-owners-object) |
| original\_response\_message\_id? | snowflake | ID of the original response message, present only on [follow-up messages](/developers/interactions/receiving-and-responding) |
| interacted\_message\_id | snowflake | ID of the message that contained the interactive component |
###### Modal Submit Interaction Metadata Structure
| Field | Type | Description |
| --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id | snowflake | ID of the interaction |
| type | [interaction type](/developers/interactions/receiving-and-responding#interaction-object-interaction-type) | Type of interaction |
| user | [user](/developers/resources/user#user-object) object | User who triggered the interaction |
| authorizing\_integration\_owners | dictionary with keys of [application integration types](/developers/resources/application#application-object-application-integration-types) | IDs for installation context(s) related to an interaction. Details in [Authorizing Integration Owners Object](/developers/interactions/receiving-and-responding#interaction-object-authorizing-integration-owners-object) |
| original\_response\_message\_id? | snowflake | ID of the original response message, present only on [follow-up messages](/developers/interactions/receiving-and-responding) |
| triggering\_interaction\_metadata | [Application Command Interaction Metadata](/developers/resources/message#message-interaction-metadata-object-application-command-interaction-metadata-structure) or [Message Component Interaction Metadata](/developers/resources/message#message-interaction-metadata-object-message-component-interaction-metadata-structure) object | Metadata for the interaction that was used to open the modal |
### Message Call Object
Information about the call in a private channel.
###### Message Call Object Structure
| Field | Type | Description |
| ----------------- | ------------------- | ------------------------------------------------------------------------------------------------ |
| participants | array of snowflakes | array of [user](/developers/resources/user#user-object) object ids that participated in the call |
| ended\_timestamp? | ?ISO8601 timestamp | time when call ended |
### Message Reference Object
#### Message Reference Structure
| Field | Type | Description |
| ---------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| type? \* | integer | [type of reference](/developers/resources/message#message-reference-types). |
| message\_id? | snowflake | id of the originating message |
| channel\_id? \*\* | snowflake | id of the originating message's channel |
| guild\_id? | snowflake | id of the originating message's guild |
| fail\_if\_not\_exists? | boolean | when sending, whether to error if the referenced message doesn't exist instead of sending as a normal (non-reply) message, default true |
\* If `type` is unset, `DEFAULT` can be assumed in order to match the behavior before message reference had types.
In future API versions this will become a required field.
\*\* `channel_id` is optional when creating a reply, but will always be present when receiving an event/response that includes this data model. **Required for forwards.**
#### Message Reference Types
Determines how associated data is populated.
| Type | Value | Coupled Message Field | Description |
| ------- | ----- | --------------------- | -------------------------------------------------------- |
| DEFAULT | 0 | `referenced_message` | A standard reference used by replies. |
| FORWARD | 1 | `message_snapshot` | Reference used to point to a message at a point in time. |
`FORWARD` can only be used for basic messages; i.e. messages which do not have strong bindings to a non global entity.
Thus we support only messages with type `DEFAULT`, `REPLY`, `CHAT_INPUT_COMMAND`, or `CONTEXT_MENU_COMMAND`, and don't support messages with a poll, call, or activity.
This is subject to change in the future.
Applications must be able to read a message's content in order to forward it. An application attempting to forward a message it cannot read will receive error code `160014` with the message "You cannot forward a message whose content you cannot read."
#### Message Reference Content Attribution
Message references are generic attribution on a message.
There are multiple message types that have a `message_reference` object.
###### Crosspost messages
* These are messages that originated from another channel (IS\_CROSSPOST flag).
* These messages have all three fields, which point to the original message that was crossposted.
###### Channel Follow Add messages
* These are automatic messages sent when a channel is followed into the current channel (type 12).
* These messages have the `channel_id` and `guild_id` fields, which point to the followed announcement channel.
###### Pin messages
* These are automatic messages sent when a message is pinned (type 6).
* These messages have `message_id` and `channel_id`, and `guild_id` if it is in a guild, which point to the message that was pinned.
###### Forwards
* These are messages which capture a snapshot of a message.
* These messages have an array of [`message_snapshot`](/developers/resources/message#message-snapshot-object) objects containing a copy of the original message. This copy follows the same structure as a message, but has only the minimal set of fields returned required for context/rendering.
* of note: `author` will be excluded
* A forwarded message can be identified by looking at its `message_reference.type` field
* `message_snapshots` will be the message data associated with the forward. Currently we support only 1 snapshot.
* prevents spoofing forwarded data
* `message_snapshots` are taken the moment a forward message is created, and are **immutable**; any mutations to the original message will not be propagated.
* Forwards are created by including a message\_reference with `FORWARD` type when sending a message.
* Required fields: `type`, `message_id`, `channel_id`
* the requestor must have `VIEW_CHANNEL` permissions
###### Replies
* These are messages replying to a previous message (type 19).
* These messages have `message_id` and `channel_id`, and `guild_id` if it is in a guild, which point to the message that was replied to. The channel\_id and guild\_id will be the same as the reply.
* Replies are created by including a message\_reference when sending a message. When sending, only `message_id` is required.
* These messages can have the referenced message resolved in the `referenced_message` field.
###### Thread Created messages
* These are automatic messages sent when a public thread is created from an old message or without a message (type 18).
* These messages have the `channel_id` and `guild_id` fields, which point to the created thread channel.
###### Thread starter messages
* These are the first message in public threads created from messages. They point back to the message in the parent channel from which the thread was started. (type 21)
* These messages have `message_id`, `channel_id`, and `guild_id`.
* These messages can have the referenced message resolved in the `referenced_message` field.
* These messages will never have content, embeds, or attachments, mainly just the `message_reference` and `referenced_message` fields.
###### Poll result messages
* These are automatic messages sent after a poll has ended and the results have been finalized. (type 46)
* These messages have `message_id` and `channel_id`, which point to the original poll message. The `channel_id` will be the same as that of the poll.
* The author will be the same as the author of the poll and will be mentioned.
* These messages contain a [`poll_result` embed](/developers/resources/message#embed-fields-by-embed-type-poll-result-embed-fields)
###### Context Menu Command messages
* These are messages sent when a user uses a context menu Application Command. (type 23)
* If the command was a [message command](/developers/interactions/application-commands#message-commands), the message will have `message_id` and `channel_id`, and `guild_id` if it is in a guild, which point to the message that the command was used on. The channel\_id and guild\_id will be the same as the new message.
* These messages can have the referenced message resolved in the `referenced_message` field.
#### Voice Messages
Voice messages are messages with the `IS_VOICE_MESSAGE` flag. They have the following properties.
* They cannot be edited.
* Only a single audio attachment is allowed. No content, stickers, etc...
* The [attachment](/developers/resources/message#attachment-object) has additional fields: `duration_secs` and `waveform`. The `Content-Type` of the attachment must begin with `audio/` to respect these fields.
The `waveform` is intended to be a preview of the entire voice message, with 1 byte per datapoint encoded in base64. Clients sample the recording at most
once per 100 milliseconds, but will downsample so that no more than 256 datapoints are in the waveform.
As of 2023-04-14, clients upload a 1 channel, 48000 Hz, 32kbps Opus stream in an OGG container.
The encoding, and the waveform details, are an implementation detail and may change without warning or documentation.
### Message Snapshot Object
#### Message Snapshot Structure
| Field | Type | Description |
| --------- | ---------------------------------------------------------------------- | ------------------------------------------------- |
| message\* | partial [message](/developers/resources/message#message-object) object | minimal subset of fields in the forwarded message |
\* The current subset of message fields consists of:
`type`, `content`, `embeds`, `attachments`, `timestamp`, `edited_timestamp`, `flags`, `mentions`, `mention_roles`, `stickers`, `sticker_items`, and `components`.
While message snapshots are able to support nested snapshots, we currently limit the depth of nesting to 1.
### Reaction Object
###### Reaction Structure
| Field | Type | Description |
| -------------- | ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| count | integer | Total number of times this emoji has been used to react (including super reacts) |
| count\_details | object | [Reaction count details object](/developers/resources/message#reaction-count-details-object) |
| me | boolean | Whether the current user reacted using this emoji |
| me\_burst | boolean | Whether the current user super-reacted using this emoji |
| emoji | partial [emoji](/developers/resources/emoji#emoji-object) object | emoji information |
| burst\_colors | array | HEX colors used for super reaction |
### Reaction Count Details Object
The reaction count details object contains a breakdown of normal and super reaction counts for the associated emoji.
###### Reaction Count Details Structure
| Field | Type | Description |
| ------ | ------- | ------------------------- |
| burst | integer | Count of super reactions |
| normal | integer | Count of normal reactions |
### Embed Object
###### Embed Structure
| Field | Type | Description |
| ------------ | ------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- |
| title? | string | title of embed |
| type? | string | [type of embed](/developers/resources/message#embed-object-embed-types) (always "rich" for webhook embeds) |
| description? | string | description of embed |
| url? | string | url of embed |
| timestamp? | ISO8601 timestamp | timestamp of embed content |
| color? | integer | color code of the embed |
| footer? | [embed footer](/developers/resources/message#embed-object-embed-footer-structure) object | footer information |
| image? | [embed image](/developers/resources/message#embed-object-embed-image-structure) object | image information |
| thumbnail? | [embed image](/developers/resources/message#embed-object-embed-image-structure) object | thumbnail information |
| video? | [embed video](/developers/resources/message#embed-object-embed-video-structure) object | video information |
| provider? | [embed provider](/developers/resources/message#embed-object-embed-provider-structure) object | provider information |
| author? | [embed author](/developers/resources/message#embed-object-embed-author-structure) object | author information |
| fields? | array of [embed field](/developers/resources/message#embed-object-embed-field-structure) objects | fields information, max of 25 |
| flags? | integer | [embed flags](/developers/resources/message#embed-object-embed-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) |
###### Embed Types
| Type | Description |
| ------------ | ------------------------------------------------------------------------------------------------------ |
| rich | generic embed rendered from embed attributes |
| image | image embed |
| video | video embed |
| gifv | animated gif image embed rendered as a video embed |
| article | article embed |
| link | link embed |
| poll\_result | [poll result embed](/developers/resources/message#embed-fields-by-embed-type-poll-result-embed-fields) |
###### Embed Flags
| Flag | Value | Description |
| ----------------------------- | -------- | -------------------------------------------------------- |
| IS\_CONTENT\_INVENTORY\_ENTRY | `1 << 5` | this embed is a fallback for a reply to an activity card |
###### Embed Video Structure
| Field | Type | Description |
| --------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| url? | string | source url of video |
| proxy\_url? | string | a proxied url of the video |
| height? | integer | height of video |
| width? | integer | width of video |
| content\_type? | string | the video's [media type](https://en.wikipedia.org/wiki/Media_type) |
| placeholder? | string | [thumbhash](https://evanw.github.io/thumbhash/) placeholder of the video |
| placeholder\_version? | integer | version of the placeholder |
| description? | string | description (alt text) for the video |
| flags? | integer | [embed media flags](/developers/resources/message#embed-object-embed-media-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) |
###### Embed Image Structure
| Field | Type | Description |
| --------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| url | string | source url of image (only supports http(s) and attachments) |
| proxy\_url? | string | a proxied url of the image |
| height? | integer | height of image |
| width? | integer | width of image |
| content\_type? | string | the image's [media type](https://en.wikipedia.org/wiki/Media_type) |
| placeholder? | string | [thumbhash](https://evanw.github.io/thumbhash/) placeholder of the image |
| placeholder\_version? | integer | version of the placeholder |
| description? | string | description (alt text) for the image |
| flags? | integer | [embed media flags](/developers/resources/message#embed-object-embed-media-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) |
###### Embed Media Flags
| Flag | Value | Description |
| ------------ | -------- | ---------------------- |
| IS\_ANIMATED | `1 << 5` | this image is animated |
###### Embed Provider Structure
| Field | Type | Description |
| ----- | ------ | ---------------- |
| name? | string | name of provider |
| url? | string | url of provider |
###### Embed Author Structure
| Field | Type | Description |
| ----------------- | ------ | ---------------------------------------------------------- |
| name | string | name of author |
| url? | string | url of author (only supports http(s)) |
| icon\_url? | string | url of author icon (only supports http(s) and attachments) |
| proxy\_icon\_url? | string | a proxied url of author icon |
###### Embed Footer Structure
| Field | Type | Description |
| ----------------- | ------ | ---------------------------------------------------------- |
| text | string | footer text |
| icon\_url? | string | url of footer icon (only supports http(s) and attachments) |
| proxy\_icon\_url? | string | a proxied url of footer icon |
###### Embed Field Structure
| Field | Type | Description |
| ------- | ------- | ----------------------------------------------- |
| name | string | name of the field |
| value | string | value of the field |
| inline? | boolean | whether or not this field should display inline |
###### Embed Limits
To facilitate showing rich content, rich embeds do not follow the traditional limits of message content. However, some limits are still in place to prevent excessively large embeds. The following table describes the limits:
All of the following limits are measured inclusively. Leading and trailing whitespace characters are not included (they are trimmed automatically).
| Field | Limit |
| -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| title | 256 characters |
| description | 4096 characters |
| fields | Up to 25 [field](/developers/resources/message#embed-object-embed-field-structure) objects |
| [field.name](/developers/resources/message#embed-object-embed-field-structure) | 256 characters |
| [field.value](/developers/resources/message#embed-object-embed-field-structure) | 1024 characters |
| [footer.text](/developers/resources/message#embed-object-embed-footer-structure) | 2048 characters |
| [author.name](/developers/resources/message#embed-object-embed-author-structure) | 256 characters |
Additionally, the combined sum of characters in all `title`, `description`, `field.name`, `field.value`, `footer.text`, and `author.name` fields across all embeds attached to a message must not exceed 6000 characters. Violating any of these constraints will result in a `Bad Request` response.
Embeds are deduplicated by URL. If a message contains multiple embeds with the same URL, only the first is shown.
#### Embed Fields by Embed Type
Certain embed types are used to power special UIs. These embeds use [fields](/developers/resources/message#embed-object-embed-field-structure) to include additional data in key-value pairs. Below is a reference of possible embed fields for each of the following embed types.
###### Poll Result Embed Fields
| Field | Description |
| -------------------------------- | ---------------------------------------------------------- |
| poll\_question\_text | question text from the original poll |
| victor\_answer\_votes | number of votes for the answer(s) with the most votes |
| total\_votes | total number of votes in the poll |
| victor\_answer\_id? | id for the winning answer |
| victor\_answer\_text? | text for the winning answer |
| victor\_answer\_emoji\_id? | id for an emoji associated with the winning answer |
| victor\_answer\_emoji\_name? | name of an emoji associated with the winning answer |
| victor\_answer\_emoji\_animated? | if an emoji associated with the winning answer is animated |
### Attachment Object
###### Attachment Structure
| Field | Type | Description |
| --------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| id | snowflake | attachment id |
| filename | string | name of file attached |
| title? | string | the title of the file |
| description? | string | description (alt text) for the file (max 1024 characters) |
| content\_type? | string | the attachment's [media type](https://en.wikipedia.org/wiki/Media_type) |
| size | integer | size of file in bytes |
| url | string | source url of file |
| proxy\_url | string | a proxied url of file |
| height? | ?integer | height of file (if image or video) |
| width? | ?integer | width of file (if image or video) |
| placeholder? | string | [thumbhash](https://evanw.github.io/thumbhash/) placeholder (if image or video) |
| placeholder\_version? | integer | version of the placeholder (if image or video) |
| ephemeral? \* | boolean | whether this attachment is ephemeral |
| duration\_secs? | float | the duration of the audio or video file |
| waveform? | string | base64 encoded bytearray representing a sampled waveform (currently for voice messages) |
| flags? | integer | [attachment flags](/developers/resources/message#attachment-object-attachment-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) |
| clip\_participants? | array of [user](/developers/resources/user#user-object) objects | for Clips, array of users who were in the stream |
| clip\_created\_at? | ISO8601 timestamp | for Clips, when the clip was created |
| application? | ?[application](/developers/resources/application#application-object) | for Clips, the application in the stream, if recognized |
\* Ephemeral attachments will automatically be removed after a set period of time. Ephemeral attachments on messages are guaranteed to be available as long as the message itself exists.
###### Attachment Request Structure
This structure is used in Message Create and Edit requests to set which attachments are in the message and provide their metadata.
When editing, you must provide the `id` of each file to keep, and you can optionally update the `description` and `is_spoiler` fields of existing attachments.
| Field | Type | Description |
| --------------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| id | snowflake or number | attachment id, for new attachments this must match the `n` in `files[n]` |
| filename? | string | name of file attached |
| title? | string | the title of the file |
| description? | string | description (alt text) for the file (max 1024 characters) |
| duration\_secs? | float | the duration of the audio or video file (required for voice messages) |
| waveform? | string | base64 encoded bytearray representing a sampled waveform (required for voice messages) |
| is\_spoiler? | boolean | whether the attachment should be marked as a spoiler and blurred until clicked, this sets the `IS_SPOILER` attachment flag |
###### Attachment Flags
| Flag | Value | Description |
| ------------- | -------- | ------------------------------------------------------------------------------------------------------------- |
| IS\_CLIP | `1 << 0` | this attachment is a [Clip from a stream](https://support.discord.com/hc/en-us/articles/16861982215703) |
| IS\_THUMBNAIL | `1 << 1` | this attachment is the thumbnail of a thread in a media channel, displayed in the grid but not on the message |
| IS\_REMIX | `1 << 2` | this attachment has been edited using the remix feature on mobile (deprecated) |
| IS\_SPOILER | `1 << 3` | this attachment was marked as a spoiler and is blurred until clicked |
| IS\_ANIMATED | `1 << 5` | this attachment is an animated image |
### Channel Mention Object
###### Channel Mention Structure
| Field | Type | Description |
| --------- | --------- | --------------------------------------------------------------------------------- |
| id | snowflake | id of the channel |
| guild\_id | snowflake | id of the guild containing the channel |
| type | integer | the [type of channel](/developers/resources/channel#channel-object-channel-types) |
| name | string | the name of the channel |
### Allowed Mentions Object
Setting the `allowed_mentions` field lets you determine whether users will receive notifications when you include mentions in the message content, or the content of components attached to that message. This field is always validated against your permissions and the presence of said mentions in the message, to avoid "phantom" pings where users receive a notification without a visible mention in the message. For example, if you want to ping everyone, including it in the `allowed_mentions` field is not enough, the mention format (`@everyone`) must also be present in the content of the message or its components. It is important to note that setting this field **does not** guarantee a push notification will be sent, as additional factors can influence this:
* To mention roles and notify their members, the role's `mentionable` field must be set to `true`, or the bot must have the `MENTION_EVERYONE` permission
* To mention `@everyone` and `@here`, the bot must have the `MENTION_EVERYONE` permission
* Setting the `SUPPRESS_NOTIFICATIONS` flag when sending a message will disable push notifications and only cause a notification badge
* Users can customize their notification settings through the Discord app, which might cause them to only receive a notification badge and no push notification
###### Allowed Mention Types
| Type | Value | Description |
| ----------------- | ---------- | ------------------------------------- |
| Role Mentions | "roles" | Controls role mentions |
| User Mentions | "users" | Controls user mentions |
| Everyone Mentions | "everyone" | Controls @everyone and @here mentions |
###### Default Settings for Allowed Mentions
The default value for the `allowed_mentions` field, used when it is not passed in the body, varies depending on the context:
In **regular messages**, all mention types are parsed, which is equivalent to sending the following data:
```json theme={"system"}
{
"parse": ["users", "roles", "everyone"]
}
```
In **interactions** and **webhooks**, only user mentions are parsed, which corresponds to the following:
```json theme={"system"}
{
"parse": ["users"]
}
```
| Field | Type | Description |
| -------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
| parse? | array of allowed mention types | An array of [allowed mention types](/developers/resources/message#allowed-mentions-object-allowed-mention-types) to parse from the content |
| roles? | array of snowflakes | Array of role ids to mention, max 100 |
| users? | array of snowflakes | Array of user ids to mention, max 100 |
| replied\_user? | boolean | For replies, whether to mention the author of the message being replied to, defaults to false |
###### Allowed Mentions Examples
Because the behavior of the `allowed_mentions` field is more complex than it seems, here's a set of examples:
In the following case, we are sending a regular message **without** configuring `allowed_mentions`. As a result, all included mentions will be parsed.
```json theme={"system"}
{
"content": "@here Hello <@&1234> and <@5678> 👋"
}
```
If you want to completely suppress all mentions in the message, you can configure the `allowed_mentions` field as we've documented above:
```json theme={"system"}
{
"content": "@here Hello <@&1234> and <@5678> 👋",
"allowed_mentions": {
"parse": []
}
}
```
It is important to note that the `parse` field is **mutually exclusive** with the other fields. In the example below, only the `1234` role and the `5678` user mentions would be parsed, but **not** the `@here` at the beginning. Passing a falsy value such as `null` or an empty array into the `users` field does not trigger a validation error.
```json theme={"system"}
{
"content": "@here Hello <@&1234> and <@5678> 👋",
"allowed_mentions": {
"parse": ["users", "roles"],
"users": []
}
}
```
In this next example, **only** `@everyone` would be parsed, as well as users `1234` and `5678` in case they suppressed `@everyone` mentions in their settings.
```json theme={"system"}
{
"content": "@everyone <@1234> <@5678> <@&789> 👋",
"allowed_mentions": {
"parse": ["everyone"],
"users": ["1234", "5678"]
}
}
```
Due to possible ambiguities, not all configurations are accepted. Here's an example of an *invalid* configuration, because it includes both `parse` and `users`, despite those fields being mutually exclusive, causing a validation error.
```json theme={"system"}
{
"content": "@everyone <@1234> <@5678> <@9012> <@&200>",
"allowed_mentions": {
"parse": ["users"],
"users": ["1234", "5678"]
}
}
```
Any entities whose id is included can be mentioned. Do note the API will silently ignore entities whose id are present in the `allowed_mentions` field, but not in the content of the message or its components. For example, in the following configuration, the user 123 mention would be parsed because it is present in the `content`. However, since there is no mention of user 456 in the `content`, they would not be notified.
```json theme={"system"}
{
"content": "<@123> Time for some memes 🤠",
"allowed_mentions": {
"users": ["123", "456"]
}
}
```
### Role Subscription Data Object
###### Role Subscription Data Object Structure
| Field | Type | Description |
| ------------------------------- | --------- | --------------------------------------------------------------------- |
| role\_subscription\_listing\_id | snowflake | the id of the sku and listing that the user is subscribed to |
| tier\_name | string | the name of the tier that the user is subscribed to |
| total\_months\_subscribed | integer | the cumulative number of months that the user has been subscribed for |
| is\_renewal | boolean | whether this notification is for a renewal rather than a new purchase |
### Message Pin Object
###### Message Pin Object Struture
| Field | Type | Description |
| ---------- | -------------------------------------------------------------- | ------------------------------- |
| pinned\_at | ISO8601 timestamp | the time the message was pinned |
| message | [message](/developers/resources/message#message-object) object | the pinned message |
### Shared Client Theme Object
###### Shared Client Theme Object Struture
| Field | Type | Description |
| --------------- | ------------------------------------------------------------- | ------------------------------------------------------ |
| colors | array of strings | the hexadecimal-encoded colors of the theme (max of 5) |
| gradient\_angle | integer | the direction of the theme's colors (max of 360) |
| base\_mix | integer | the intensity of the theme's colors (max of 100) |
| base\_theme? | ?[base theme](/developers/resources/message#base-theme-types) | the mode of the theme |
###### Base Theme Types
| Type | Value |
| ---------- | ----- |
| UNSET \[1] | 0 |
| DARK | 1 |
| LIGHT | 2 |
| DARKER | 3 |
| MIDNIGHT | 4 |
\[1] Equivalent to the `DARK` type.
###### Example Shared Client Theme
```json theme={"system"}
{
"shared_client_theme": {
"colors": ["5865F2", "7258F2", "9858F2", "BE58F2", "E558F2"],
"gradient_angle": 0,
"base_mix": 58,
"base_theme": 1
}
}
```
## Get Channel Messages
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/messages
Retrieves the messages in a channel. Returns an array of [message](/developers/resources/message#message-object) objects from newest to oldest on success.
If operating on a guild channel, this endpoint requires the current user to have the `VIEW_CHANNEL` permission. If the channel is a voice channel, they must *also* have the `CONNECT` permission.
If the current user is missing the `READ_MESSAGE_HISTORY` permission in the channel, then no messages will be returned.
The `before`, `after`, and `around` parameters are mutually exclusive, only one may be passed at a time.
###### Query String Params
| Field | Type | Description | Default |
| ------- | --------- | ---------------------------------------- | ------- |
| around? | snowflake | Get messages around this message ID | absent |
| before? | snowflake | Get messages before this message ID | absent |
| after? | snowflake | Get messages after this message ID | absent |
| limit? | integer | Max number of messages to return (1-100) | 50 |
## Search Guild Messages
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/messages/search
Returns a list of messages without the `reactions` key that match a search query in the guild. Requires the `READ_MESSAGE_HISTORY` permission.
This endpoint is restricted according to whether the `MESSAGE_CONTENT` [Privileged Intent](/developers/events/gateway#privileged-intents) is enabled for your application.
If the entity you are searching is not yet indexed, the endpoint will return a 202 accepted response. The response body will not contain any search results, and will look similar to an error response:
```json theme={"system"}
{
"message": "Index not yet available. Try again later",
"code": 110000,
"documents_indexed": 0,
"retry_after": 2
}
```
You should retry the request after the timeframe specified in the `retry_after` field. If the `retry_after` field is `0`, you should retry the request after a short delay.
Due to speed optimizations, search may return slightly fewer results than the limit specified when messages have not been accessed for a long time.
Clients should not rely on the length of the `messages` array to paginate results.
Additionally, when messages are actively being created or deleted, the `total_results` field may not be accurate.
###### Query String Params
| Field | Type | Description |
| ------------------------- | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| limit? | integer | Max number of messages to return (1-25, default 25) |
| offset? | integer | Number to offset the returned messages by (max 9975) |
| max\_id? | snowflake | Get messages before this message ID |
| min\_id? | snowflake | Get messages after this message ID |
| slop? | integer | Max number of words to skip between matching tokens in the search `content` (max 100, default 2) |
| content? | string | Filter messages by content (max 1024 characters) |
| channel\_id? | [array](/developers/reference#array-query-strings) of snowflakes | Filter messages by these channels (max 500) |
| author\_type? | [array](/developers/reference#array-query-strings) of strings | Filter messages by [author type](/developers/resources/message#search-guild-messages-author-types) |
| author\_id? | [array](/developers/reference#array-query-strings) of snowflakes | Filter messages by these authors (max 100) |
| mentions? | [array](/developers/reference#array-query-strings) of snowflakes | Filter messages that mention these users (max 100) |
| mentions\_role\_id? | [array](/developers/reference#array-query-strings) of snowflakes | Filter messages that mention these roles (max 100) |
| mention\_everyone? | [boolean](/developers/reference#boolean-query-strings) | Filter messages that do or do not mention @everyone |
| replied\_to\_user\_id? | [array](/developers/reference#array-query-strings) of snowflakes | Filter messages that reply to these users (max 100) |
| replied\_to\_message\_id? | [array](/developers/reference#array-query-strings) of snowflakes | Filter messages that reply to these messages (max 100) |
| pinned? | [boolean](/developers/reference#boolean-query-strings) | Filter messages by whether they are or are not pinned |
| has? | [array](/developers/reference#array-query-strings) of strings | Filter messages by whether or not they [have specific things](/developers/resources/message#search-guild-messages-search-has-types) |
| embed\_type? | [array](/developers/reference#array-query-strings) of strings | Filter messages by [embed type](/developers/resources/message#search-guild-messages-search-embed-types) |
| embed\_provider? | [array](/developers/reference#array-query-strings) of strings | Filter messages by embed provider (case-sensitive, e.g. `Tenor`) (max 256 characters, max 100) |
| link\_hostname? | [array](/developers/reference#array-query-strings) of strings | Filter messages by link hostname (e.g. `discordapp.com`) (max 256 characters, max 100) |
| attachment\_filename? | [array](/developers/reference#array-query-strings) of strings | Filter messages by attachment filename (max 1024 characters, max 100) |
| attachment\_extension? | [array](/developers/reference#array-query-strings) of strings | Filter messages by attachment extension (e.g. `txt`) (max 256 characters, max 100) |
| sort\_by? \[1] | string | The [sorting algorithm](/developers/resources/message#search-guild-messages-search-sort-modes) to use |
| sort\_order? \[1] | string | The direction to sort (`asc` or `desc`, default `desc`) |
| include\_nsfw? | [boolean](/developers/reference#boolean-query-strings) | Whether to include results from age-restricted channels (default false) |
\[1] Sort order is not respected when sorting by relevance.
###### Author Types
All types can be negated by prefixing them with `-`, which means results will not include messages that match the type.
| Type | Description |
| ------- | ------------------------------------- |
| user | Return messages sent by user accounts |
| bot | Return messages sent by bot accounts |
| webhook | Return messages sent by webhooks |
###### Search Has Types
All types can be negated by prefixing them with `-`, which means results will not include messages that match the type.
| Type | Description |
| -------- | --------------------------------------------- |
| image | Return messages that have an image |
| sound | Return messages that have a sound attachment |
| video | Return messages that have a video |
| file | Return messages that have an attachment |
| sticker | Return messages that have a sent sticker |
| embed | Return messages that have an embed |
| link | Return messages that have a link |
| poll | Return messages that have a poll |
| snapshot | Return messages that have a forwarded message |
###### Search Embed Types
These do not correspond 1:1 to actual [embed types](/developers/resources/message#embed-object-embed-types) and encompass a wider range of actual types.
| Type | Description |
| -------- | ------------------------------------------ |
| image | Return messages that have an image embed |
| video | Return messages that have a video embed |
| gif \[1] | Return messages that have a gifv embed |
| sound | Return messages that have a sound embed |
| article | Return messages that have an article embed |
\[1] Messages sent before February 24, 2026 may not be properly indexed under the `gif` embed type.
###### Search Sort Modes
| Type | Description |
| --------- | -------------------------------------------------------- |
| timestamp | Sort by the message creation time (default) |
| relevance | Sort by the relevance of the message to the search query |
###### Response Body
| Field | Type | Description |
| ------------------------------ | ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------- |
| doing\_deep\_historical\_index | boolean | Whether the guild is undergoing a deep historical indexing operation |
| documents\_indexed? | integer | The number of documents that have been indexed during the current index operation, if any |
| total\_results | integer | The total number of results that match the query |
| messages \[1] | array of array of [message](/developers/resources/message#message-object) objects | A nested array of messages that match the query |
| threads? | array of [channel](/developers/resources/channel#channel-object) objects | The threads that contain the returned messages |
| members? | array of [thread member](/developers/resources/channel#thread-member-object) objects | A thread member object for each returned thread the current user has joined |
\[1] The nested array was used to provide surrounding context to search results. However, surrounding context is no longer returned.
## Get Channel Message
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/messages/[\{message.id}](/developers/resources/message#message-object)
Retrieves a specific message in the channel. Returns a [message](/developers/resources/message#message-object) object on success.
If operating on a guild channel, this endpoint requires the current user to have the `VIEW_CHANNEL` and `READ_MESSAGE_HISTORY` permissions. If the channel is a voice channel, they must *also* have the `CONNECT` permission.
## Create Message
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/messages
Discord may strip certain characters from message content, like invalid unicode characters or characters which cause unexpected message formatting. If you are passing user-generated strings into message content, consider sanitizing the data to prevent unexpected behavior and using `allowed_mentions` to prevent unexpected mentions.
Post a message to a guild text or DM channel. Returns a [message](/developers/resources/message#message-object) object. Fires a [Message Create](/developers/events/gateway-events#message-create) Gateway event. See [message formatting](/developers/reference#message-formatting) for more information on how to properly format messages.
To create a message as a reply or forward of another message, apps can include a [`message_reference`](/developers/resources/message#message-reference-structure).
Refer to the documentation for required fields.
Files must be attached using a `multipart/form-data` body as described in [Uploading Files](/developers/reference#uploading-files).
###### Limitations
* When operating on a guild channel, the current user must have the `SEND_MESSAGES` permission.
* When sending a message with `tts` (text-to-speech) set to `true`, the current user must have the `SEND_TTS_MESSAGES` permission.
* When creating a message as a reply to another message, the current user must have the `READ_MESSAGE_HISTORY` permission.
* The referenced message must exist and cannot be a system message.
* The maximum request size when sending a message is **25 MiB**
* For the embed object, you can set every field except `type` (it will be `rich` regardless of if you try to set it), `provider`, `video`, and any `height`, `width`, or `proxy_url` values for images.
###### JSON/Form Params
| Field | Type | Description |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| content?\* | string | Message contents (up to 2000 characters) |
| nonce? | integer or string | Can be used to verify a message was sent (up to 25 characters). Value will appear in the [Message Create event](/developers/events/gateway-events#message-create). |
| tts? | boolean | `true` if this is a TTS message |
| embeds?\* | array of [embed](/developers/resources/message#embed-object) objects | Up to 10 `rich` embeds (up to 6000 characters) |
| allowed\_mentions? | [allowed mention object](/developers/resources/message#allowed-mentions-object) | Allowed mentions for the message |
| message\_reference?\* | [message reference](/developers/resources/message#message-reference-structure) | Include to make your message a reply or a forward |
| components?\* | array of [message component](/developers/components/reference#component-object) objects | Components to include with the message |
| sticker\_ids?\* | array of snowflakes | IDs of up to 3 [stickers](/developers/resources/sticker#sticker-object) in the server to send in the message |
| files\[n]?\* | file contents | Contents of the file being sent. See [Uploading Files](/developers/reference#uploading-files) |
| payload\_json? | string | JSON-encoded body of non-file params, only for `multipart/form-data` requests. See [Uploading Files](/developers/reference#uploading-files) |
| attachments? | array of partial [attachment request](/developers/resources/message#attachment-object-attachment-request-structure) objects | Metadata for the attachments. See [Uploading Files](/developers/reference#uploading-files) |
| flags?\*\* | integer | [Message flags](/developers/resources/message#message-object-message-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) (only `SUPPRESS_EMBEDS`, `SUPPRESS_NOTIFICATIONS`, `IS_VOICE_MESSAGE`, and `IS_COMPONENTS_V2` can be set) |
| enforce\_nonce? | boolean | If true and nonce is present, it will be checked for uniqueness in the past few minutes. If another message was created by the same author with the same nonce, that message will be returned and no new message will be created. |
| poll? | [poll](/developers/resources/poll#poll-create-request-object) request object | A poll! |
| shared\_client\_theme? | [shared client theme object](/developers/resources/message#shared-client-theme-object) | The custom client-side theme to share via the message |
\* At least one of `content`, `embeds`, `sticker_ids`, `components`, `files[n]`, `poll`, or `shared_client_theme` is required. When forwarding a message, only `message_reference` is required.
\*\* When the flag `IS_COMPONENTS_V2` is set, the message can only contain `components`. Providing `content`, `embeds`, `sticker_ids`, `poll`, or `shared_client_theme` will fail with a 400 BAD REQUEST response.
###### Example Request Body (application/json)
```json theme={"system"}
{
"content": "Hello, World!",
"tts": false,
"embeds": [{
"title": "Hello, Embed!",
"description": "This is an embedded message."
}]
}
```
Examples for file uploads are available in [Uploading Files](/developers/reference#uploading-files).
## Crosspost Message
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/messages/[\{message.id}](/developers/resources/message#message-object)/crosspost
Crosspost a message in an Announcement Channel to following channels. This endpoint requires the `SEND_MESSAGES` permission, if the current user sent the message, or additionally the `MANAGE_MESSAGES` permission, for all other messages, to be present for the current user.
Returns a [message](/developers/resources/message#message-object) object. Fires a [Message Update](/developers/events/gateway-events#message-update) Gateway event.
## Create Reaction
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/messages/[\{message.id}](/developers/resources/message#message-object)/reactions/[\{emoji.id}](/developers/resources/emoji#emoji-object)/@me
Create a reaction for the message. This endpoint requires the `READ_MESSAGE_HISTORY` permission to be present on the current user. Additionally, if nobody else has reacted to the message using this emoji, this endpoint requires the `ADD_REACTIONS` permission to be present on the current user. Returns a 204 empty response on success. Fires a [Message Reaction Add](/developers/events/gateway-events#message-reaction-add) Gateway event.
The `emoji` must be [URL Encoded](https://en.wikipedia.org/wiki/Percent-encoding) or the request will fail with `10014: Unknown Emoji`. To use custom emoji, you must encode it in the format `name:id` with the emoji name and emoji id.
## Delete Own Reaction
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/messages/[\{message.id}](/developers/resources/message#message-object)/reactions/[\{emoji.id}](/developers/resources/emoji#emoji-object)/@me
Delete a reaction the current user has made for the message. Returns a 204 empty response on success. Fires a [Message Reaction Remove](/developers/events/gateway-events#message-reaction-remove) Gateway event.
The `emoji` must be [URL Encoded](https://en.wikipedia.org/wiki/Percent-encoding) or the request will fail with `10014: Unknown Emoji`. To use custom emoji, you must encode it in the format `name:id` with the emoji name and emoji id.
## Delete User Reaction
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/messages/[\{message.id}](/developers/resources/message#message-object)/reactions/[\{emoji.id}](/developers/resources/emoji#emoji-object)/[\{user.id}](/developers/resources/user#user-object)
Deletes another user's reaction. This endpoint requires the `MANAGE_MESSAGES` permission to be present on the current user. Returns a 204 empty response on success. Fires a [Message Reaction Remove](/developers/events/gateway-events#message-reaction-remove) Gateway event.
The `emoji` must be [URL Encoded](https://en.wikipedia.org/wiki/Percent-encoding) or the request will fail with `10014: Unknown Emoji`. To use custom emoji, you must encode it in the format `name:id` with the emoji name and emoji id.
## Get Reactions
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/messages/[\{message.id}](/developers/resources/message#message-object)/reactions/[\{emoji.id}](/developers/resources/emoji#emoji-object)
Get a list of users that reacted with this emoji. Returns an array of [user](/developers/resources/user#user-object) objects on success.
The `emoji` must be [URL Encoded](https://en.wikipedia.org/wiki/Percent-encoding) or the request will fail with `10014: Unknown Emoji`. To use custom emoji, you must encode it in the format `name:id` with the emoji name and emoji id.
###### Query String Params
| Field | Type | Description | Default |
| ------ | --------- | ---------------------------------------------------------------------------------- | ------- |
| type? | integer | The [type of reaction](/developers/resources/message#get-reactions-reaction-types) | 0 |
| after? | snowflake | Get users after this user ID | absent |
| limit? | integer | Max number of users to return (1-100) | 25 |
###### Reaction Types
| Type | Value |
| ------ | ----- |
| NORMAL | 0 |
| BURST | 1 |
## Delete All Reactions
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/messages/[\{message.id}](/developers/resources/message#message-object)/reactions
Deletes all reactions on a message. This endpoint requires the `MANAGE_MESSAGES` permission to be present on the current user. Fires a [Message Reaction Remove All](/developers/events/gateway-events#message-reaction-remove-all) Gateway event.
## Delete All Reactions for Emoji
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/messages/[\{message.id}](/developers/resources/message#message-object)/reactions/[\{emoji.id}](/developers/resources/emoji#emoji-object)
Deletes all the reactions for a given emoji on a message. This endpoint requires the `MANAGE_MESSAGES` permission to be present on the current user. Fires a [Message Reaction Remove Emoji](/developers/events/gateway-events#message-reaction-remove-emoji) Gateway event.
The `emoji` must be [URL Encoded](https://en.wikipedia.org/wiki/Percent-encoding) or the request will fail with `10014: Unknown Emoji`. To use custom emoji, you must encode it in the format `name:id` with the emoji name and emoji id.
## Edit Message
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/messages/[\{message.id}](/developers/resources/message#message-object)
Edit a previously sent message. The fields `content`, `embeds`, `flags` and `components` can be edited by the original message author. Other users can only edit `flags` and only if they have the `MANAGE_MESSAGES` permission in the corresponding channel. When specifying flags, ensure to include all previously set flags/bits in addition to ones that you are modifying. Only `flags` documented in the table below may be modified by users (unsupported flag changes are currently ignored without error).
When the `content` field is edited, the arrays `mentions` and `mention_roles` and the boolean `mention_everyone` in the message object will be reconstructed from scratch based on the new content. When the message flag `IS_COMPONENTS_V2` is set, the reconstructed arrays and boolean are based on the edited content in the `components` array. The `allowed_mentions` field of the edit request controls how this happens. If there is no explicit `allowed_mentions` in the edit request, the content will be parsed with *default* allowances, that is, without regard to whether or not an `allowed_mentions` was present in the request that originally created the message.
Returns a [message](/developers/resources/message#message-object) object. Fires a [Message Update](/developers/events/gateway-events#message-update) Gateway event.
Refer to [Uploading Files](/developers/reference#uploading-files) for details on attachments and `multipart/form-data` requests.
Any provided files will be **appended** to the message. To remove or replace files you will have to supply the `attachments` field which specifies the files to retain on the message after edit.
Starting with API v10, the `attachments` array must contain all attachments that should be present after edit, including **retained and new** attachments provided in the request body.
All parameters to this endpoint are optional and nullable.
###### JSON/Form Params
| Field | Type | Description |
| ----------------- | ------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| content | string | Message contents (up to 2000 characters) |
| embeds | array of [embed](/developers/resources/message#embed-object) objects | Up to 10 `rich` embeds (up to 6000 characters) |
| flags\* | integer | Edit the [flags](/developers/resources/message#message-object-message-flags) of a message (`SUPPRESS_EMBEDS` and `IS_COMPONENTS_V2` only) |
| allowed\_mentions | [allowed mention object](/developers/resources/message#allowed-mentions-object) | Allowed mentions for the message |
| components | array of [message component](/developers/components/reference#component-object) | Components to include with the message |
| files\[n] | file contents | Contents of the file being sent/edited. See [Uploading Files](/developers/reference#uploading-files) |
| payload\_json | string | JSON-encoded body of non-file params (multipart/form-data only). See [Uploading Files](/developers/reference#uploading-files) |
| attachments | array of [attachment request](/developers/resources/message#attachment-object-attachment-request-structure) objects | Attached files to keep and their metadata. See [Uploading Files](/developers/reference#uploading-files) |
\* The `SUPPRESS_EMBEDS` flag can be both set and unset, while the `IS_COMPONENTS_V2` flag can only be set. When the `IS_COMPONENTS_V2` flag is set, any of the used `content`, `embeds`, `sticker_ids`, or `poll` fields must have their values reset to empty. For `content` and `poll` this is `null`. For `embeds` and `sticker_ids` this is `[]`. Failing to do this will result in a 400 BAD REQUEST response.
## Delete Message
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/messages/[\{message.id}](/developers/resources/message#message-object)
Delete a message. If operating on a guild channel and trying to delete a message that was not sent by the current user, this endpoint requires the `MANAGE_MESSAGES` permission. Returns a 204 empty response on success. Fires a [Message Delete](/developers/events/gateway-events#message-delete) Gateway event.
This endpoint supports the `X-Audit-Log-Reason` header.
## Bulk Delete Messages
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/messages/bulk-delete
Delete multiple messages in a single request. This endpoint can only be used on guild channels and requires the `MANAGE_MESSAGES` permission. Returns a 204 empty response on success. Fires a [Message Delete Bulk](/developers/events/gateway-events#message-delete-bulk) Gateway event.
Any message IDs given that do not exist or are invalid will count towards the minimum and maximum message count (currently 2 and 100 respectively).
This endpoint will not delete messages older than 2 weeks, and will fail with a 400 BAD REQUEST if any message provided is older than that or if any duplicate message IDs are provided.
This endpoint supports the `X-Audit-Log-Reason` header.
###### JSON Params
| Field | Type | Description |
| -------- | ------------------- | ----------------------------------------- |
| messages | array of snowflakes | an array of message ids to delete (2-100) |
## Get Channel Pins
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/messages/pins
Retrieves the list of pins in a channel. Requires the `VIEW_CHANNEL` permission. If the user is missing the `READ_MESSAGE_HISTORY` permission in the channel, then no pins will be returned.
###### Query String Params
| Field | Type | Description | Default |
| ------- | ----------------- | ----------------------------------------- | ------- |
| before? | ISO8601 timestamp | Get messages pinned before this timestamp | absent |
| limit? | integer | Max number of pins to return (1-50) | 50 |
###### Response Structure
| Field | Type |
| --------- | -------------------------------------------------------------------------------- |
| items | array of [message pin](/developers/resources/message#message-pin-object) objects |
| has\_more | boolean |
###### Example
If you want to get 100 pins you'd send these two requests:
`GET /channels/:id/messages/pins?limit=50`
`GET /channels/:id/messages/pins?limit=50&before={pins[pins.len() - 1].pinned_at}`
## Pin Message
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/messages/pins/[\{message.id}](/developers/resources/message#message-object)
Pin a message in a channel. Requires the `PIN_MESSAGES` permission. Fires a [Channel Pins Update](/developers/events/gateway-events#channel-pins-update) Gateway event.
This endpoint supports the `X-Audit-Log-Reason` header.
## Unpin Message
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/messages/pins/[\{message.id}](/developers/resources/message#message-object)
Unpin a message in a channel. Requires the `PIN_MESSAGES` permission. Returns a 204 empty response on success. Fires a [Channel Pins Update](/developers/events/gateway-events#channel-pins-update) Gateway event.
This endpoint supports the `X-Audit-Log-Reason` header.
## Get Pinned Messages (deprecated)
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/pins
Gets the first 50 pinned messages in a channel, returning an array of [message](/developers/resources/message#message-object) objects on success.
This endpoint is deprecated. Use [Get Channel Pins](/developers/resources/message#get-channel-pins) instead.
## Pin Message (deprecated)
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/pins/[\{message.id}](/developers/resources/message#message-object)
This endpoint is deprecated. Use [Pin Message](/developers/resources/message#pin-message) instead.
## Unpin Message (deprecated)
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/pins/[\{message.id}](/developers/resources/message#message-object)
This endpoint is deprecated. Use [Unpin Message](/developers/resources/message#unpin-message) instead.
# Poll Resource
Source: https://docs.discord.com/developers/resources/poll
Reference for Discord poll objects and management endpoints.
A poll is... well... a poll! It holds information about a poll!
### Poll Object
The poll object has a lot of levels and nested structures. It was also designed
to support future extensibility, so some fields may appear to be more complex than
necessary.
###### Poll Object Structure
| Field | Type | Description |
| ------------------ | --------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| question | [Poll Media Object](/developers/resources/poll#poll-media-object-poll-media-object-structure) | The question of the poll. Only `text` is supported. |
| answers | List of [Poll Answer Objects](/developers/resources/poll#poll-answer-object-poll-answer-object-structure) | Each of the answers available in the poll. |
| expiry | ?IS08601 timestamp | The time when the poll ends. |
| allow\_multiselect | boolean | Whether a user can select multiple answers |
| layout\_type | integer | The [layout type](/developers/resources/poll#layout-type) of the poll |
| results? | [Poll Results Object](/developers/resources/poll#poll-results-object-poll-results-object-structure) | The results of the poll |
`expiry` is marked as nullable to support non-expiring polls in the future, but all polls have an expiry currently.
### Poll Create Request Object
This is the request object used when creating a poll across the different endpoints.
It is similar but not exactly identical to the main [poll object](/developers/resources/poll#poll-object-poll-object-structure).
The main difference is that the request has `duration` which eventually becomes `expiry`.
###### Poll Create Request Object Structure
| Field | Type | Description |
| ------------------- | --------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| question | [Poll Media Object](/developers/resources/poll#poll-media-object-poll-media-object-structure) | The question of the poll. Only `text` is supported. |
| answers | List of [Poll Answer Objects](/developers/resources/poll#poll-answer-object-poll-answer-object-structure) | Each of the answers available in the poll, up to 10 |
| duration? | integer | Number of hours the poll should be open for, up to 32 days. Defaults to 24 |
| allow\_multiselect? | boolean | Whether a user can select multiple answers. Defaults to false |
| layout\_type? | integer | The [layout type](/developers/resources/poll#layout-type) of the poll. Defaults to... DEFAULT! |
### Layout Type
We might have different layouts for polls in the future.
For now though, this number will be 1.
| Type | ID | Description |
| ------- | -- | ------------------------------ |
| DEFAULT | 1 | The, uhm, default layout type. |
### Poll Media Object
The poll media object is a common object that backs both the question and answers.
The intention is that it allows us to extensibly add new ways to display things in the future.
For now, `question` only supports `text`, while answers can have an optional `emoji`.
###### Poll Media Object Structure
| Field | Type | Description |
| ------ | --------------------------------------------------------- | ---------------------- |
| text? | string | The text of the field |
| emoji? | partial [emoji](/developers/resources/emoji#emoji-object) | The emoji of the field |
`text` should always be non-null for both questions and answers, but please do not depend on that in the future.
The maximum length of `text` is 300 for the question, and 55 for any answer.
When creating a poll answer with an emoji, one only needs to send either the `id` (custom emoji) or `name` (default emoji) as the only field.
### Poll Answer Object
The `answer_id` is a number that labels each answer.
As an implementation detail, it currently starts at 1 for the first answer and goes up sequentially.
We recommend against depending on this sequence.
Currently, there is a maximum of 10 answers per poll.
###### Poll Answer Object Structure
| Field | Type | Description |
| ------------ | --------------------------------------------------------------------------------------------- | ---------------------- |
| answer\_id\* | integer | The ID of the answer |
| poll\_media | [Poll Media Object](/developers/resources/poll#poll-media-object-poll-media-object-structure) | The data of the answer |
\* Only sent as part of responses from Discord's API/Gateway.
### Poll Results Object
In a nutshell, this contains the number of votes for each answer.
The `results` field may be not present in certain responses where, as an implementation detail, we do not fetch the poll results in our backend.
This should be treated as "unknown results", as opposed to "no results". You can keep using the results if you have previously received them through other means.
Also due to the intricacies of counting at scale, while a poll is in progress the results may not be perfectly accurate.
They usually are accurate, and shouldn't deviate significantly -- it's just difficult to make guarantees.
To compensate for this, after a poll is finished there is a background job which performs a final, accurate tally of votes.
This tally concludes once `is_finalized` is `true`. Polls that have ended will also always contain results.
If `answer_counts` does not contain an entry for a particular answer, then there are no votes for that answer.
###### Poll Results Object Structure
| Field | Type | Description |
| -------------- | --------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- |
| is\_finalized | boolean | Whether the votes have been precisely counted |
| answer\_counts | List of [Poll Answer Count Object](/developers/resources/poll#poll-results-object-poll-answer-count-object-structure) | The counts for each answer |
###### Poll Answer Count Object Structure
| Field | Type | Description |
| --------- | ------- | ---------------------------------------------- |
| id | integer | The `answer_id` |
| count | integer | The number of votes for this answer |
| me\_voted | boolean | Whether the current user voted for this answer |
# Poll Endpoints
For creating a poll, see [Create Message](/developers/resources/message#create-message). After creation, the poll message cannot be edited.
Apps are not allowed to vote on polls. No rights! :)
## Get Answer Voters
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/polls/[\{message.id}](/developers/resources/message#message-object)/answers/[\{answer\_id}](/developers/resources/poll#poll-answer-object)
Get a list of users that voted for this specific answer.
###### Query String Params
| Field | Type | Description | Default |
| ------ | --------- | ------------------------------------- | ------- |
| after? | snowflake | Get users after this user ID | absent |
| limit? | integer | Max number of users to return (1-100) | 25 |
###### Response Body
| Field | Type | Description |
| ----- | ------------------------------------------------------- | ------------------------------- |
| users | array of [user](/developers/resources/user#user-object) | Users who voted for this answer |
## End Poll
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/polls/[\{message.id}](/developers/resources/message#message-object)/expire
Immediately ends the poll. You cannot end polls from other users.
Returns a [message](/developers/resources/message#message-object) object. Fires a [Message Update](/developers/events/gateway-events#message-update) Gateway event.
# SKU Resource
Source: https://docs.discord.com/developers/resources/sku
Reference for Discord SKU objects that represent premium app offerings.
SKUs (stock-keeping units) in Discord represent premium offerings that can be made available to your application's users or guilds.
### SKU Object
###### SKU Structure
| Field | Type | Description |
| --------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------- |
| id | snowflake | ID of SKU |
| type | integer | [Type of SKU](/developers/resources/sku#sku-object-sku-types) |
| application\_id | snowflake | ID of the parent application |
| name | string | Customer-facing name of your premium offering |
| slug | string | System-generated URL slug based on the SKU's name |
| flags | integer | [SKU flags](/developers/resources/sku#sku-object-sku-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) |
###### SKU Example
```json theme={"system"}
{
"id": "1088510058284990888",
"type": 5,
"dependent_sku_id": null,
"application_id": "788708323867885999",
"manifest_labels": null,
"access_type": 1,
"name": "Test Premium",
"features": [],
"release_date": null,
"premium": false,
"slug": "test-premium",
"flags": 128,
"show_age_gate": false
}
```
###### SKU Types
For subscriptions, SKUs will have a type of either `SUBSCRIPTION` represented by `type: 5` or `SUBSCRIPTION_GROUP` represented by `type:6`. For any current implementations, you will want to use the SKU defined by `type: 5`. A `SUBSCRIPTION_GROUP` is automatically created for each `SUBSCRIPTION` SKU and are not used at this time.
| Type | Value | Description |
| ------------------- | ----- | -------------------------------------------------------- |
| DURABLE | 2 | Durable one-time purchase |
| CONSUMABLE | 3 | Consumable one-time purchase |
| SUBSCRIPTION | 5 | Represents a recurring subscription |
| SUBSCRIPTION\_GROUP | 6 | System-generated group for each SUBSCRIPTION SKU created |
###### SKU Flags
For subscriptions, there are two types of access levels you can offer to users:
* **Guild Subscriptions**: A subscription purchased by a user and applied to a single server. Everyone in that server gets your premium benefits.
* **User Subscriptions**: A subscription purchased by a user for themselves. They get access to your premium benefits in every server.
The `flags` field can be used to differentiate user and server subscriptions with a bitwise `&` operator.
| Type | Value | Description |
| ------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------- |
| AVAILABLE | `1 << 2` | SKU is available for purchase |
| GUILD\_SUBSCRIPTION | `1 << 7` | Recurring SKU that can be purchased by a user and applied to a single server. Grants access to every user in that server. |
| USER\_SUBSCRIPTION | `1 << 8` | Recurring SKU purchased by a user for themselves. Grants access to the purchasing user in every server. |
## List SKUs
/applications/[\{application.id}](/developers/resources/application#application-object)/skus
Returns all SKUs for a given application.
Because of how our SKU and subscription systems work, you will see two SKUs for your subscription offering. For integration and testing entitlements for Subscriptions, you should use the SKU with `type: 5`.
```json theme={"system"}
[
{
"id": "1088510053843210999",
"type": 6,
"dependent_sku_id": null,
"application_id": "788708323867885999",
"manifest_labels": null,
"access_type": 1,
"name": "Test Premium",
"features": [],
"release_date": null,
"premium": false,
"slug": "test-premium",
"flags": 128,
"show_age_gate": false
},
{
"id": "1088510058284990888",
"type": 5,
"dependent_sku_id": null,
"application_id": "788708323867885999",
"manifest_labels": null,
"access_type": 1,
"name": "Test Premium",
"features": [],
"release_date": null,
"premium": false,
"slug": "test-premium",
"flags": 128,
"show_age_gate": false
}
]
```
# Soundboard Resource
Source: https://docs.discord.com/developers/resources/soundboard
Reference for Discord soundboard objects and management endpoints.
Users can play soundboard sounds in voice channels, triggering a [Voice Channel Effect Send](/developers/events/gateway-events#voice-channel-effect-send) Gateway event for users connected to the voice channel.
There is a set of [default sounds](/developers/resources/soundboard#list-default-soundboard-sounds) available to all users. Soundboard sounds can also be [created in a guild](/developers/resources/soundboard#create-guild-soundboard-sound); users will be able to use the sounds in the guild, and Nitro subscribers can use them in all guilds.
Soundboard sounds in a set of guilds can be retrieved over the Gateway using [Request Soundboard Sounds](/developers/events/gateway-events#request-soundboard-sounds).
### Soundboard Sound Object
###### Soundboard Sound Structure
| Field | Type | Description |
| ----------- | ----------------------------------------------------- | ------------------------------------------------------------------------- |
| name | string | the name of this sound |
| sound\_id | snowflake | the id of this sound |
| volume | double | the volume of this sound, from 0 to 1 |
| emoji\_id | ?snowflake | the id of this sound's custom emoji |
| emoji\_name | ?string | the unicode character of this sound's standard emoji |
| guild\_id? | snowflake | the id of the guild this sound is in |
| available | boolean | whether this sound can be used, may be false due to loss of Server Boosts |
| user? | [user](/developers/resources/user#user-object) object | the user who created this sound |
###### Example Default Soundboard Sound
```json theme={"system"}
{
"name": "quack",
"sound_id": "1",
"volume": 1.0,
"emoji_id": null,
"emoji_name": "🦆",
"available": true
}
```
###### Example Guild Soundboard Sound
```json theme={"system"}
{
"name": "Yay",
"sound_id": "1106714396018884649",
"volume": 1,
"emoji_id": "989193655938064464",
"emoji_name": null,
"guild_id": "613425648685547541",
"available": true
}
```
### Sound Files
A soundboard sound can be retrieved in MP3 or Ogg format at the URL:
```
https://cdn.discordapp.com/soundboard-sounds/{sound_id}
```
## Send Soundboard Sound
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/send-soundboard-sound
Send a soundboard sound to a voice channel the user is connected to. Fires a [Voice Channel Effect Send](/developers/events/gateway-events#voice-channel-effect-send) Gateway event.
Requires the `SPEAK` and `USE_SOUNDBOARD` permissions, and also the `USE_EXTERNAL_SOUNDS` permission if the sound is from a different server. Additionally, requires the user to be connected to the voice channel, having a [voice state](/developers/resources/voice#voice-state-object) without `deaf`, `self_deaf`, `mute`, or `suppress` enabled.
###### JSON Params
| Field | Type | Description |
| ------------------ | --------- | ------------------------------------------------------------------------------------------------ |
| sound\_id | snowflake | the id of the soundboard sound to play |
| source\_guild\_id? | snowflake | the id of the guild the soundboard sound is from, required to play sounds from different servers |
## List Default Soundboard Sounds
/soundboard-default-sounds
Returns an array of [soundboard sound](/developers/resources/soundboard#soundboard-sound-object) objects that can be used by all users.
## List Guild Soundboard Sounds
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/soundboard-sounds
Returns a list of the guild's soundboard sounds. Includes `user` fields if the bot has the `CREATE_GUILD_EXPRESSIONS` or `MANAGE_GUILD_EXPRESSIONS` permission.
###### Response Structure
| Field | Type |
| ----- | --------------------------------------------------------------------------------------------- |
| items | array of [soundboard sound](/developers/resources/soundboard#soundboard-sound-object) objects |
## Get Guild Soundboard Sound
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/soundboard-sounds/[\{sound.id}](/developers/resources/soundboard#soundboard-sound-object)
Returns a [soundboard sound](/developers/resources/soundboard#soundboard-sound-object) object for the given sound id. Includes the `user` field if the bot has the `CREATE_GUILD_EXPRESSIONS` or `MANAGE_GUILD_EXPRESSIONS` permission.
## Create Guild Soundboard Sound
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/soundboard-sounds
Create a new soundboard sound for the guild. Requires the `CREATE_GUILD_EXPRESSIONS` permission. Returns the new [soundboard sound](/developers/resources/soundboard#soundboard-sound-object) object on success. Fires a [Guild Soundboard Sound Create](/developers/events/gateway-events#guild-soundboard-sound-create) Gateway event.
Soundboard sounds have a max file size of 512kb and a max duration of 5.2 seconds.
This endpoint supports the `X-Audit-Log-Reason` header.
###### JSON
###### JSON Params
| Field | Type | Description |
| ------------ | ---------- | ---------------------------------------------------------------------------------------------------- |
| name | string | name of the soundboard sound (2-32 characters) |
| sound | data uri | the mp3 or ogg sound data, base64 encoded, similar to [image data](/developers/reference#image-data) |
| volume? | ?double | the volume of the soundboard sound, from 0 to 1, defaults to 1 |
| emoji\_id? | ?snowflake | the id of the custom emoji for the soundboard sound |
| emoji\_name? | ?string | the unicode character of a standard emoji for the soundboard sound |
## Modify Guild Soundboard Sound
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/soundboard-sounds/[\{sound.id}](/developers/resources/soundboard#soundboard-sound-object)
Modify the given soundboard sound. For sounds created by the current user, requires either the `CREATE_GUILD_EXPRESSIONS` or `MANAGE_GUILD_EXPRESSIONS` permission. For other sounds, requires the `MANAGE_GUILD_EXPRESSIONS` permission. Returns the updated [soundboard sound](/developers/resources/soundboard#soundboard-sound-object) object on success. Fires a [Guild Soundboard Sound Update](/developers/events/gateway-events#guild-soundboard-sound-update) Gateway event.
All parameters to this endpoint are optional.
This endpoint supports the `X-Audit-Log-Reason` header.
###### JSON Params
| Field | Type | Description |
| ----------- | ---------- | ------------------------------------------------------------------ |
| name | string | name of the soundboard sound (2-32 characters) |
| volume | ?double | the volume of the soundboard sound, from 0 to 1 |
| emoji\_id | ?snowflake | the id of the custom emoji for the soundboard sound |
| emoji\_name | ?string | the unicode character of a standard emoji for the soundboard sound |
## Delete Guild Soundboard Sound
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/soundboard-sounds/[\{sound.id}](/developers/resources/soundboard#soundboard-sound-object)
Delete the given soundboard sound. For sounds created by the current user, requires either the `CREATE_GUILD_EXPRESSIONS` or `MANAGE_GUILD_EXPRESSIONS` permission. For other sounds, requires the `MANAGE_GUILD_EXPRESSIONS` permission. Returns `204 No Content` on success. Fires a [Guild Soundboard Sound Delete](/developers/events/gateway-events#guild-soundboard-sound-delete) Gateway event.
This endpoint supports the `X-Audit-Log-Reason` header.
# Stage Instance Resource
Source: https://docs.discord.com/developers/resources/stage-instance
Reference for Discord stage instance objects for managing live audio events.
A Stage Instance holds information about a live stage.
### Stage Instance Object
###### Stage Instance Structure
| Field | Type | Description |
| --------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------- |
| id | snowflake | The id of this Stage instance |
| guild\_id | snowflake | The guild id of the associated Stage channel |
| channel\_id | snowflake | The id of the associated Stage channel |
| topic | string | The topic of the Stage instance (1-120 characters) |
| privacy\_level | integer | The [privacy level](/developers/resources/stage-instance#stage-instance-object-privacy-level) of the Stage instance |
| discoverable\_disabled | boolean | Whether or not Stage Discovery is disabled (deprecated) |
| guild\_scheduled\_event\_id | ?snowflake | The id of the scheduled event for this Stage instance |
###### Privacy Level
| Level | Value | Description |
| ----------- | ----- | ---------------------------------------------------- |
| PUBLIC | 1 | The Stage instance is visible publicly. (deprecated) |
| GUILD\_ONLY | 2 | The Stage instance is visible to only guild members. |
###### Example Stage Instance
```json theme={"system"}
{
"id": "840647391636226060",
"guild_id": "197038439483310086",
"channel_id": "733488538393510049",
"topic": "Testing Testing, 123",
"privacy_level": 1,
"discoverable_disabled": false,
"guild_scheduled_event_id": "947656305244532806"
}
```
## Definitions
Below are some definitions related to stages.
* **Liveness:** A Stage channel is considered *live* when there is an associated stage instance. Conversely, a Stage channel is *not live* when there is no associated stage instance.
* **Speakers:** A participant of a Stage channel is a *speaker* when their [voice state](/developers/resources/voice#voice-state-object)
is not `suppress`ed, and has no `request_to_speak_timestamp`.
* **Moderators**: A member of the guild is a *moderator* of a Stage channel if they have all of the following [permissions](/developers/topics/permissions):
* `MANAGE_CHANNELS`
* `MUTE_MEMBERS`
* `MOVE_MEMBERS`
* **Topic**: This is the blurb that gets shown below the channel's name, among other places.
* **Public**: A Stage instance is public when it has a `privacy_level` of `PUBLIC`. While a guild has a public Stage instance:
* Users in the Stage can have the Stage show in their [activities](/developers/events/gateway-events#presence).
* [Invites](/developers/resources/invite#invite-object) to the Stage channel will have the `stage_instance` field.
## Auto Closing
When a Stage channel has no speakers for a certain period of time (on the order of minutes), the Stage instance will be automatically deleted.
## Create Stage Instance
/stage-instances
Creates a new Stage instance associated to a Stage channel. Returns that [Stage instance](/developers/resources/stage-instance#stage-instance-object-stage-instance-structure). Fires a [Stage Instance Create](/developers/events/gateway-events#stage-instance-create) Gateway event.
Requires the user to be a moderator of the Stage channel.
This endpoint supports the `X-Audit-Log-Reason` header.
###### JSON Params
| Field | Type | Description |
| ----------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| channel\_id | snowflake | The id of the Stage channel |
| topic | string | The topic of the Stage instance (1-120 characters) |
| privacy\_level? | integer | The [privacy level](/developers/resources/stage-instance#stage-instance-object-privacy-level) of the Stage instance (default GUILD\_ONLY) |
| send\_start\_notification? \* | boolean | Notify @everyone that a Stage instance has started |
| guild\_scheduled\_event\_id? | snowflake | The guild scheduled event associated with this Stage instance |
\* The stage moderator must have the `MENTION_EVERYONE` permission for this notification to be sent.
## Get Stage Instance
/stage-instances/[\{channel.id}](/developers/resources/channel#channel-object)
Gets the stage instance associated with the Stage channel, if it exists.
## Modify Stage Instance
/stage-instances/[\{channel.id}](/developers/resources/channel#channel-object)
Updates fields of an existing Stage instance. Returns the updated [Stage instance](/developers/resources/stage-instance#stage-instance-object-stage-instance-structure). Fires a [Stage Instance Update](/developers/events/gateway-events#stage-instance-update) Gateway event.
Requires the user to be a moderator of the Stage channel.
This endpoint supports the `X-Audit-Log-Reason` header.
###### JSON Params
| Field | Type | Description |
| --------------- | ------- | ------------------------------------------------------------------------------------------------------------------- |
| privacy\_level? | integer | The [privacy level](/developers/resources/stage-instance#stage-instance-object-privacy-level) of the Stage instance |
## Delete Stage Instance
/stage-instances/[\{channel.id}](/developers/resources/channel#channel-object)
Deletes the Stage instance. Returns `204 No Content`. Fires a [Stage Instance Delete](/developers/events/gateway-events#stage-instance-delete) Gateway event.
Requires the user to be a moderator of the Stage channel.
This endpoint supports the `X-Audit-Log-Reason` header.
# Sticker Resource
Source: https://docs.discord.com/developers/resources/sticker
Reference for Discord sticker objects and management endpoints.
### Sticker Object
Represents a sticker that can be sent in messages.
###### Sticker Structure
| Field | Type | Description |
| ------------ | ----------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| id | snowflake | [id of the sticker](/developers/reference#image-formatting) |
| pack\_id? | snowflake | for standard stickers, id of the pack the sticker is from |
| name | string | name of the sticker |
| description | ?string | description of the sticker |
| tags\* | string | autocomplete/suggestion tags for the sticker (max 200 characters) |
| type | integer | [type of sticker](/developers/resources/sticker#sticker-object-sticker-types) |
| format\_type | integer | [type of sticker format](/developers/resources/sticker#sticker-object-sticker-format-types) |
| available? | boolean | whether this guild sticker can be used, may be false due to loss of Server Boosts |
| guild\_id? | snowflake | id of the guild that owns this sticker |
| user? | [user](/developers/resources/user#user-object) object | the user that uploaded the guild sticker |
| sort\_value? | integer | the standard sticker's sort order within its pack |
\* A comma separated list of keywords is the format used in this field by standard stickers, but this is just a convention.
Incidentally the client will always use a name generated from an emoji as the value of this field when creating or modifying a guild sticker.
###### Sticker Types
| Type | Value | Description |
| -------- | ----- | ----------------------------------------------------- |
| STANDARD | 1 | an official sticker in a pack |
| GUILD | 2 | a sticker uploaded to a guild for the guild's members |
###### Sticker Format Types
| Type | Value |
| ------ | ----- |
| PNG | 1 |
| APNG | 2 |
| LOTTIE | 3 |
| GIF | 4 |
###### Example Sticker
```json theme={"system"}
{
"id": "749054660769218631",
"name": "Wave",
"tags": "wumpus, hello, sup, hi, oi, heyo, heya, yo, greetings, greet, welcome, wave, :wave, :hello, :hi, :hey, hey, \ud83d\udc4b, \ud83d\udc4b\ud83c\udffb, \ud83d\udc4b\ud83c\udffc, \ud83d\udc4b\ud83c\udffd, \ud83d\udc4b\ud83c\udffe, \ud83d\udc4b\ud83c\udfff, goodbye, bye, see ya, later, laterz, cya",
"type": 1,
"format_type": 3,
"description": "Wumpus waves hello",
"pack_id": "847199849233514549",
"sort_value": 12
}
```
### Sticker Item Object
The smallest amount of data required to render a sticker. A partial sticker object.
###### Sticker Item Structure
| Field | Type | Description |
| ------------ | --------- | ------------------------------------------------------------------------------------------- |
| id | snowflake | id of the sticker |
| name | string | name of the sticker |
| format\_type | integer | [type of sticker format](/developers/resources/sticker#sticker-object-sticker-format-types) |
### Sticker Pack Object
Represents a pack of standard stickers.
###### Sticker Pack Structure
| Field | Type | Description |
| ------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------- |
| id | snowflake | id of the sticker pack |
| stickers | array of [sticker](/developers/resources/sticker#sticker-object) objects | the stickers in the pack |
| name | string | name of the sticker pack |
| sku\_id | snowflake | id of the pack's SKU |
| cover\_sticker\_id? | snowflake | id of a sticker in the pack which is shown as the pack's icon |
| description | string | description of the sticker pack |
| banner\_asset\_id? | snowflake | id of the sticker pack's [banner image](/developers/reference#image-formatting) |
###### Example Sticker Pack
```json theme={"system"}
{
"id": "847199849233514549",
"stickers": [],
"name": "Wumpus Beyond",
"sku_id": "847199849233514547",
"cover_sticker_id": "749053689419006003",
"description": "Say hello to Wumpus!",
"banner_asset_id": "761773777976819732"
}
```
## Get Sticker
/stickers/[\{sticker.id}](/developers/resources/sticker#sticker-object)
Returns a [sticker](/developers/resources/sticker#sticker-object) object for the given sticker ID.
## List Sticker Packs
/sticker-packs
Returns a list of available sticker packs.
###### Response Structure
| Field | Type |
| -------------- | ---------------------------------------------------------------------------------- |
| sticker\_packs | array of [sticker pack](/developers/resources/sticker#sticker-pack-object) objects |
## Get Sticker Pack
/sticker-packs/[\{pack.id}](/developers/resources/sticker#sticker-pack-object)
Returns a [sticker pack](/developers/resources/sticker#sticker-pack-object) object for the given sticker pack ID.
## List Guild Stickers
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/stickers
Returns an array of [sticker](/developers/resources/sticker#sticker-object) objects for the given guild. Includes `user` fields if the bot has the `CREATE_GUILD_EXPRESSIONS` or `MANAGE_GUILD_EXPRESSIONS` permission.
## Get Guild Sticker
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/stickers/[\{sticker.id}](/developers/resources/sticker#sticker-object)
Returns a [sticker](/developers/resources/sticker#sticker-object) object for the given guild and sticker IDs. Includes the `user` field if the bot has the `CREATE_GUILD_EXPRESSIONS` or `MANAGE_GUILD_EXPRESSIONS` permission.
## Create Guild Sticker
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/stickers
Create a new sticker for the guild. Send a `multipart/form-data` body. Requires the `CREATE_GUILD_EXPRESSIONS` permission. Returns the new [sticker](/developers/resources/sticker#sticker-object) object on success. Fires a [Guild Stickers Update](/developers/events/gateway-events#guild-stickers-update) Gateway event.
Every guilds has five free sticker slots by default, and each Boost level will grant access to more slots.
This endpoint supports the `X-Audit-Log-Reason` header.
Lottie stickers can only be uploaded on guilds that have either the `VERIFIED` and/or the `PARTNERED` [guild feature](/developers/resources/guild#guild-object-guild-features).
Uploaded stickers are constrained to 5 seconds in length for animated stickers, and 320 x 320 pixels.
###### Form Params
| Field | Type | Description |
| ----------- | ------------- | -------------------------------------------------------------------------------------- |
| name | string | name of the sticker (2-30 characters) |
| description | string | description of the sticker (empty or 2-100 characters) |
| tags | string | autocomplete/suggestion tags for the sticker (max 200 characters) |
| file | file contents | the sticker file to upload, must be a PNG, APNG, GIF, or Lottie JSON file, max 512 KiB |
## Modify Guild Sticker
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/stickers/[\{sticker.id}](/developers/resources/sticker#sticker-object)
Modify the given sticker. For stickers created by the current user, requires either the `CREATE_GUILD_EXPRESSIONS` or `MANAGE_GUILD_EXPRESSIONS` permission. For other stickers, requires the `MANAGE_GUILD_EXPRESSIONS` permission. Returns the updated [sticker](/developers/resources/sticker#sticker-object) object on success. Fires a [Guild Stickers Update](/developers/events/gateway-events#guild-stickers-update) Gateway event.
All parameters to this endpoint are optional.
This endpoint supports the `X-Audit-Log-Reason` header.
###### JSON Params
| Field | Type | Description |
| ----------- | ------- | ----------------------------------------------------------------- |
| name | string | name of the sticker (2-30 characters) |
| description | ?string | description of the sticker (2-100 characters) |
| tags | string | autocomplete/suggestion tags for the sticker (max 200 characters) |
## Delete Guild Sticker
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/stickers/[\{sticker.id}](/developers/resources/sticker#sticker-object)
Delete the given sticker. For stickers created by the current user, requires either the `CREATE_GUILD_EXPRESSIONS` or `MANAGE_GUILD_EXPRESSIONS` permission. For other stickers, requires the `MANAGE_GUILD_EXPRESSIONS` permission. Returns `204 No Content` on success. Fires a [Guild Stickers Update](/developers/events/gateway-events#guild-stickers-update) Gateway event.
This endpoint supports the `X-Audit-Log-Reason` header.
# Subscription Resource
Source: https://docs.discord.com/developers/resources/subscription
Reference for Discord subscription objects that represent recurring payments.
Subscriptions in Discord represent a user making recurring payments for at least one SKU over an ongoing period. Successful payments grant the user access to entitlements associated with the SKU.
## Subscription Object
| Field | Type | Description |
| ---------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| id | snowflake | ID of the subscription |
| user\_id | snowflake | ID of the user who is subscribed |
| sku\_ids | array of snowflakes | List of SKUs subscribed to |
| entitlement\_ids | array of snowflakes | List of entitlements granted for this subscription |
| renewal\_sku\_ids | ?array of snowflakes | List of SKUs that this user will be subscribed to at renewal |
| current\_period\_start | ISO8601 timestamp | Start of the current subscription period |
| current\_period\_end | ISO8601 timestamp | End of the current subscription period |
| status | SubscriptionStatus | Current status of the subscription |
| canceled\_at | ?ISO8601 timestamp | When the subscription was canceled |
| country? | string | ISO3166-1 alpha-2 country code of the payment source used to purchase the subscription. Missing unless queried with a private OAuth scope. |
The start of a subscription is determined by its ID. When the subscription renews, its current period is updated.
If the user cancels the subscription, the subscription will enter the `ENDING` status and the `canceled_at` timestamp will reflect the time of the cancellation.
### Subscription Example
```json theme={"system"}
{
"id": "1278078770116427839",
"user_id": "1088605110638227537",
"sku_ids": ["1158857122189168803"],
"entitlement_ids": [],
"renewal_sku_ids": null,
"current_period_start": "2024-08-27T19:48:44.406602+00:00",
"current_period_end": "2024-09-27T19:48:44.406602+00:00",
"status": 0,
"canceled_at": null
}
```
### Subscription Statuses
| Type | Value | Description |
| -------- | ----- | ----------------------------------------------- |
| ACTIVE | 0 | Subscription is active and scheduled to renew. |
| INACTIVE | 1 | Subscription is inactive and not being charged. |
| ENDING | 2 | Subscription is active but will not renew. |
Subscription status should not be used to grant perks. Use [entitlements](/developers/resources/entitlement#entitlement-object) as an indication of whether a user should have access to a specific SKU. See our guide on [Implementing App Subscriptions](/developers/monetization/implementing-app-subscriptions) for more information.
Subscriptions can start and change between any of these statuses within the current period. A subscription can be `ACTIVE` outside its current period or `INACTIVE` within its current period.
Some examples of this behavior include:
* While a failed payment is being retried, the subscription would remain `ACTIVE` until it succeeds or our system determines the payment is not recoverable.
* A refund or chargeback during the current period would make the subscription `INACTIVE`.
## List SKU Subscriptions
/skus/[\{sku.id}](/developers/resources/sku#sku-object)/subscriptions
Returns all subscriptions containing the SKU, filtered by user. Returns a list of [subscription](/developers/resources/subscription#subscription-object) objects.
### Query String Params
| Field | Type | Description | Default |
| --------- | --------- | ----------------------------------------------------------------------------- | ------- |
| before? | snowflake | List subscriptions before this ID | absent |
| after? | snowflake | List subscriptions after this ID | absent |
| limit? | integer | Number of results to return (1-100) | 50 |
| user\_id? | snowflake | User ID for which to return subscriptions. Required except for OAuth queries. | absent |
## Get SKU Subscription
/skus/[\{sku.id}](/developers/resources/sku#sku-object)/subscriptions/[\{subscription.id}](/developers/resources/subscription#subscription-object)
Get a subscription by its ID. Returns a [subscription](/developers/resources/subscription#subscription-object) object.
# User Resource
Source: https://docs.discord.com/developers/resources/user
Reference for Discord user objects and management endpoints.
Users in Discord are generally considered the base entity. Users can spawn across the entire platform, be members of
guilds, participate in text and voice chat, and much more. Users are separated by a distinction of "bot" vs "normal." Although they are similar, bot users are automated users that are "owned" by another user. Unlike normal users, bot users do
*not* have a limitation on the number of Guilds they can be a part of.
## Usernames and Nicknames
Discord enforces the following restrictions for usernames and nicknames:
1. Names can contain most valid unicode characters. We limit some zero-width and non-rendering characters.
2. Usernames must be between 2 and 32 characters long.
3. Nicknames must be between 1 and 32 characters long.
4. Names are sanitized and trimmed of leading, trailing, and excessive internal whitespace.
The following restrictions are additionally enforced for usernames:
1. Usernames cannot contain the following substrings: `@`, `#`, `:`, ` ``` `, `discord`
2. Usernames cannot be: `everyone`, `here`
There are other rules and restrictions not shared here for the sake of spam and abuse mitigation, but the majority of users won't encounter them. It's important to properly handle all error messages returned by Discord when editing or updating names.
### User Object
###### User Structure
| Field | Type | Description | Required OAuth2 Scope |
| ------------------------- | ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------- | --------------------- |
| id | snowflake | the user's id | identify |
| username | string | the user's username, not unique across the platform | identify |
| discriminator | string | the user's Discord-tag | identify |
| global\_name | ?string | the user's display name, if it is set | identify |
| avatar | ?string | the user's [avatar hash](/developers/reference#image-formatting) | identify |
| bot? | boolean | whether the user belongs to an OAuth2 application | identify |
| system? | boolean | whether the user is an Official Discord System user (part of the urgent message system) | identify |
| mfa\_enabled? | boolean | whether the user has two factor enabled on their account | identify |
| banner? | ?string | the user's [banner hash](/developers/reference#image-formatting) | identify |
| accent\_color? | ?integer | the user's banner color encoded as an integer representation of hexadecimal color code | identify |
| locale? | string | the user's chosen [language option](/developers/reference#locales) | identify |
| verified? | boolean | whether the email on this account has been verified | email |
| email? | ?string | the user's email | email |
| flags? | integer | the [flags](/developers/resources/user#user-object-user-flags) on a user's account | identify |
| premium\_type? | integer | the [type of Nitro subscription](/developers/resources/user#user-object-premium-types) on a user's account | identify.premium |
| public\_flags? | integer | the public [flags](/developers/resources/user#user-object-user-flags) on a user's account | identify |
| avatar\_decoration\_data? | ?[avatar decoration data](/developers/resources/user#avatar-decoration-data-object) object | data for the user's avatar decoration | identify |
| collectibles? | ?[collectibles](/developers/resources/user#collectibles) object | data for the user's collectibles | identify |
| primary\_guild? | ?[user primary guild](/developers/resources/user#user-object-user-primary-guild) object | the user's primary guild | identify |
###### Example User
```json theme={"system"}
{
"id": "80351110224678912",
"username": "Nelly",
"global_name": null,
"discriminator": "1337",
"avatar": "8342729096ea3675442027381ff50dfe",
"verified": true,
"email": "nelly@discord.com",
"flags": 64,
"banner": "06c16474723fe537c283b8efa61a30c8",
"accent_color": 16711680,
"premium_type": 0,
"public_flags": 64,
"avatar_decoration_data": {
"sku_id": "1144058844004233369",
"asset": "a_fed43ab12698df65902ba06727e20c0e"
},
"collectibles": {
"nameplate": {
"sku_id": "2247558840304243311",
"asset": "nameplates/nameplates/twilight/",
"label": "",
"palette": "cobalt"
}
},
"primary_guild": {
"identity_guild_id": "1234647491267808778",
"identity_enabled": true,
"tag": "DISC",
"badge": "7d1734ae5a615e82bc7a4033b98fade8"
}
}
```
###### User Flags
| Value | Name | Description |
| --------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `1 << 0` | STAFF | Discord Employee |
| `1 << 1` | PARTNER | Partnered Server Owner |
| `1 << 2` | HYPESQUAD | HypeSquad Events Member |
| `1 << 3` | BUG\_HUNTER\_LEVEL\_1 | Bug Hunter Level 1 |
| `1 << 6` | HYPESQUAD\_ONLINE\_HOUSE\_1 | House Bravery Member |
| `1 << 7` | HYPESQUAD\_ONLINE\_HOUSE\_2 | House Brilliance Member |
| `1 << 8` | HYPESQUAD\_ONLINE\_HOUSE\_3 | House Balance Member |
| `1 << 9` | PREMIUM\_EARLY\_SUPPORTER | Early Nitro Supporter |
| `1 << 10` | TEAM\_PSEUDO\_USER | User is a [team](/developers/topics/teams) |
| `1 << 14` | BUG\_HUNTER\_LEVEL\_2 | Bug Hunter Level 2 |
| `1 << 16` | VERIFIED\_BOT | Verified Bot |
| `1 << 17` | VERIFIED\_DEVELOPER | Early Verified Bot Developer |
| `1 << 18` | CERTIFIED\_MODERATOR | Moderator Programs Alumni |
| `1 << 19` | BOT\_HTTP\_INTERACTIONS | Bot uses only [HTTP interactions](/developers/interactions/receiving-and-responding#receiving-an-interaction) and is shown in the online member list |
###### Premium Types
Premium types denote the level of premium a user has. Visit the [Nitro](https://discord.com/nitro) page to learn more about the premium plans we currently offer.
| Value | Name |
| ----- | ------------- |
| 0 | None |
| 1 | Nitro Classic |
| 2 | Nitro |
| 3 | Nitro Basic |
###### User Primary Guild
| Field | Type | Description |
| ------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| identity\_guild\_id | ?snowflake | the id of the user's primary guild |
| identity\_enabled | ?boolean | whether the user is displaying the primary guild's server tag. This can be `null` if the system clears the identity, e.g. the server no longer supports tags. This will be `false` if the user manually removes their tag. |
| tag | ?string | the text of the user's server tag. Limited to 4 characters |
| badge | ?string | the [server tag badge hash](/developers/reference#image-formatting) |
### Avatar Decoration Data Object
The data for the user's [avatar decoration](https://support.discord.com/hc/en-us/articles/13410113109911-Avatar-Decorations).
###### Avatar Decoration Data Structure
| Field | Type | Description |
| ------- | --------- | -------------------------------------------------------------------- |
| asset | string | the [avatar decoration hash](/developers/reference#image-formatting) |
| sku\_id | snowflake | id of the avatar decoration's SKU |
### Collectibles
The collectibles the user has, excluding Avatar Decorations and Profile Effects.
###### Collectible Structure
| Field | Type | Description |
| ---------- | ------ | -------------------------------------------------------------------------------------------- |
| nameplate? | object | object mapping of [nameplate data](/developers/resources/user#nameplate-nameplate-structure) |
### Nameplate
The nameplate the user has.
###### Nameplate Structure
| Field | Type | Description |
| ------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| sku\_id | snowflake | id of the nameplate SKU |
| asset | string | path to the [nameplate asset](/developers/reference#image-formatting) |
| label | string | the label of this nameplate. Currently unused |
| palette | string | background color of the nameplate, one of: `crimson`, `berry`, `sky`, `teal`, `forest`, `bubble_gum`, `violet`, `cobalt`, `clover`, `lemon`, `white` |
### Connection Object
The connection object that the user has attached.
###### Connection Structure
| Field | Type | Description |
| -------------- | ------- | ---------------------------------------------------------------------------------------------- |
| id | string | id of the connection account |
| name | string | the username of the connection account |
| type | string | the [service](/developers/resources/user#connection-object-services) of this connection |
| revoked? | boolean | whether the connection is revoked |
| integrations? | array | an array of partial [server integrations](/developers/resources/guild#integration-object) |
| verified | boolean | whether the connection is verified |
| friend\_sync | boolean | whether friend sync is enabled for this connection |
| show\_activity | boolean | whether activities related to this connection will be shown in presence updates |
| two\_way\_link | boolean | whether this connection has a corresponding third party OAuth2 token |
| visibility | integer | [visibility](/developers/resources/user#connection-object-visibility-types) of this connection |
###### Services
| Value | Name |
| ------------ | ------------------- |
| amazon-music | Amazon Music |
| battlenet | Battle.net |
| bungie | Bungie.net |
| bluesky | Bluesky |
| crunchyroll | Crunchyroll |
| domain | Domain |
| ebay | eBay |
| epicgames | Epic Games |
| facebook | Facebook |
| github | GitHub |
| instagram \* | Instagram |
| mastodon | Mastodon |
| paypal | PayPal |
| playstation | PlayStation Network |
| reddit | Reddit |
| roblox | Roblox |
| spotify | Spotify |
| skype \* | Skype |
| steam | Steam |
| tiktok | TikTok |
| twitch | Twitch |
| twitter | X (Twitter) |
| xbox | Xbox |
| youtube | YouTube |
\* Service can no longer be added by users
###### Visibility Types
| Value | Name | Description |
| ----- | -------- | ------------------------------------------------ |
| 0 | None | invisible to everyone except the user themselves |
| 1 | Everyone | visible to everyone |
### Application Role Connection Object
The role connection object that an application has attached to a user.
###### Application Role Connection Structure
| Field | Type | Description |
| -------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| platform\_name | ?string | the vanity name of the platform a bot has connected (max 50 characters) |
| metadata | object | object mapping [application role connection metadata](/developers/resources/application-role-connection-metadata#application-role-connection-metadata-object) keys to their `string`-ified value (max 100 characters) for the user on the platform a bot has connected |
## Get Current User
/users/@me
Returns the [user](/developers/resources/user#user-object) object of the requester's account. For OAuth2, this requires the `identify` scope, which will return the object *without* an email, and optionally the `email` scope, which returns the object *with* an email if the user has one.
## Get User
/users/[\{user.id}](/developers/resources/user#user-object)
Returns a [user](/developers/resources/user#user-object) object for a given user ID.
## Modify Current User
/users/@me
Modify the requester's user account settings. Returns a [user](/developers/resources/user#user-object) object on success. Fires a [User Update](/developers/events/gateway-events#user-update) Gateway event.
All parameters to this endpoint are optional.
###### JSON Params
| Field | Type | Description |
| -------- | ----------------------------------------------- | -------------------------------------------------------------------------------- |
| username | string | user's username, if changed may cause the user's discriminator to be randomized. |
| avatar | ?[image data](/developers/reference#image-data) | if passed, modifies the user's avatar |
| banner | ?[image data](/developers/reference#image-data) | if passed, modifies the user's banner |
## Get Current User Guilds
/users/@me/guilds
Returns a list of partial [guild](/developers/resources/guild#guild-object) objects the current user is a member of. For OAuth2, requires the `guilds` scope.
###### Example Partial Guild
```json theme={"system"}
{
"id": "80351110224678912",
"name": "1337 Krew",
"icon": "8342729096ea3675442027381ff50dfe",
"banner": "bb42bdc37653b7cf58c4c8cc622e76cb",
"owner": true,
"permissions": "36953089",
"features": ["COMMUNITY", "NEWS", "ANIMATED_ICON", "INVITE_SPLASH", "BANNER", "ROLE_ICONS"],
"approximate_member_count": 3268,
"approximate_presence_count": 784
}
```
This endpoint returns 200 guilds by default, which is the maximum number of guilds a non-bot user can join. Therefore, pagination is **not needed** for integrations that need to get a list of the users' guilds.
###### Query String Params
| Field | Type | Description | Required | Default |
| ------------ | ------------------------------------------------------ | ---------------------------------------------------------- | -------- | ------- |
| before | snowflake | get guilds before this guild ID | false | absent |
| after | snowflake | get guilds after this guild ID | false | absent |
| limit | integer | max number of guilds to return (1-200) | false | 200 |
| with\_counts | [boolean](/developers/reference#boolean-query-strings) | include approximate member and presence counts in response | false | false |
## Get Current User Guild Member
/users/@me/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/member
Returns a [guild member](/developers/resources/guild#guild-member-object) object for the current user. Requires the `guilds.members.read` OAuth2 scope.
## Leave Guild
/users/@me/guilds/[\{guild.id}](/developers/resources/guild#guild-object)
Leave a guild. Returns a 204 empty response on success. Fires a [Guild Delete](/developers/events/gateway-events#guild-delete) Gateway event and a [Guild Member Remove](/developers/events/gateway-events#guild-member-remove) Gateway event.
## Create DM
/users/@me/channels
Create a new DM channel with a user. Returns a [DM channel](/developers/resources/channel#channel-object) object (if one already exists, it will be returned instead).
You should not use this endpoint to DM everyone in a server about something. DMs should generally be initiated by a user action. If you open a significant amount of DMs too quickly, your bot may be rate limited or blocked from opening new ones.
###### JSON Params
| Field | Type | Description |
| ------------- | --------- | --------------------------------------- |
| recipient\_id | snowflake | the recipient to open a DM channel with |
## Create Group DM
/users/@me/channels
Create a new group DM channel with multiple users. Returns a [DM channel](/developers/resources/channel#channel-object) object. This endpoint was intended to be used with the now-deprecated GameBridge SDK. Fires a [Channel Create](/developers/events/gateway-events#channel-create) Gateway event.
This endpoint is limited to 10 active group DMs.
###### JSON Params
| Field | Type | Description |
| -------------- | ---------------- | ---------------------------------------------------------------------- |
| access\_tokens | array of strings | access tokens of users that have granted your app the `gdm.join` scope |
| nicks | dict | a dictionary of user ids to their respective nicknames |
## Get Current User Connections
/users/@me/connections
Returns a list of [connection](/developers/resources/user#connection-object) objects. Requires the `connections` OAuth2 scope.
## Get Current User Application Role Connection
/users/@me/applications/[\{application.id}](/developers/resources/application#application-object)/role-connection
Returns the [application role connection](/developers/resources/user#application-role-connection-object) for the user. Requires an OAuth2 access token with `role_connections.write` scope for the application specified in the path.
## Update Current User Application Role Connection
/users/@me/applications/[\{application.id}](/developers/resources/application#application-object)/role-connection
Updates and returns the [application role connection](/developers/resources/user#application-role-connection-object) for the user. Requires an OAuth2 access token with `role_connections.write` scope for the application specified in the path.
###### JSON Params
| Field | Type | Description |
| ------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| platform\_name? | string | the vanity name of the platform a bot has connected (max 50 characters) |
| platform\_username? | string | the username on the platform a bot has connected (max 100 characters) |
| metadata? | object | object mapping [application role connection metadata](/developers/resources/application-role-connection-metadata#application-role-connection-metadata-object) keys to their `string`-ified value (max 100 characters) for the user on the platform a bot has connected |
## Delete Current User Application Role Connection
/users/@me/applications/[\{application.id}](/developers/resources/application#application-object)/role-connection
Deletes the [application role connection](/developers/resources/user#application-role-connection-object) for the user. Requires an OAuth2 access token with `role_connections.write` scope for the application specified in the path.
# Voice Resource
Source: https://docs.discord.com/developers/resources/voice
Reference for Discord voice objects and connection endpoints.
### Voice State Object
Used to represent a user's voice connection status.
###### Voice State Structure
| Field | Type | Description |
| ----------------------------- | ---------------------------------------------------------------------- | ------------------------------------------------- |
| guild\_id? | snowflake | the guild id this voice state is for |
| channel\_id | ?snowflake | the channel id this user is connected to |
| user\_id | snowflake | the user id this voice state is for |
| member? | [guild member](/developers/resources/guild#guild-member-object) object | the guild member this voice state is for |
| session\_id | string | the session id for this voice state |
| deaf | boolean | whether this user is deafened by the server |
| mute | boolean | whether this user is muted by the server |
| self\_deaf | boolean | whether this user is locally deafened |
| self\_mute | boolean | whether this user is locally muted |
| self\_stream? | boolean | whether this user is streaming using "Go Live" |
| self\_video | boolean | whether this user's camera is enabled |
| suppress | boolean | whether this user's permission to speak is denied |
| request\_to\_speak\_timestamp | ?ISO8601 timestamp | the time at which the user requested to speak |
###### Example Voice State
```json theme={"system"}
{
"channel_id": "157733188964188161",
"user_id": "80351110224678912",
"session_id": "90326bd25d71d39b9ef95b299e3872ff",
"deaf": false,
"mute": false,
"self_deaf": false,
"self_mute": true,
"suppress": false,
"request_to_speak_timestamp": "2021-03-31T18:45:31.297561+00:00"
}
```
### Voice Region Object
###### Voice Region Structure
| Field | Type | Description |
| ---------- | ------- | --------------------------------------------------------------------- |
| id | string | unique ID for the region |
| name | string | name of the region |
| optimal | boolean | true for a single server that is closest to the current user's client |
| deprecated | boolean | whether this is a deprecated voice region (avoid switching to these) |
| custom | boolean | whether this is a custom voice region (used for events/etc) |
## List Voice Regions
/voice/regions
Returns an array of [voice region](/developers/resources/voice#voice-region-object) objects that can be used when setting a voice or stage channel's [`rtc_region`](/developers/resources/channel#channel-object-channel-structure).
## Get Current User Voice State
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/voice-states/@me
Returns the current user's [voice state](/developers/resources/voice#voice-state-object) in the guild.
## Get User Voice State
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/voice-states/[\{user.id}](/developers/resources/user#user-object)
Returns the specified user's [voice state](/developers/resources/voice#voice-state-object) in the guild.
If the specified user is connected to a voice channel, the current user must have permission to connect to the channel.
## Modify Current User Voice State
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/voice-states/@me
Updates the current user's voice state. Returns `204 No Content` on success. Fires a [Voice State Update](/developers/events/gateway-events#voice-state-update) Gateway event.
###### JSON Params
| Field | Type | Description |
| ------------------------------ | ------------------ | ---------------------------------------------- |
| channel\_id? | snowflake | the id of the channel the user is currently in |
| suppress? | boolean | toggles the user's suppress state |
| request\_to\_speak\_timestamp? | ?ISO8601 timestamp | sets the user's request to speak |
###### Caveats
There are currently several caveats for this endpoint:
* `channel_id` must currently point to a stage channel.
* current user must already have joined `channel_id`.
* You must have the `MUTE_MEMBERS` permission to unsuppress yourself. You can always suppress yourself.
* You must have the `REQUEST_TO_SPEAK` permission to request to speak. You can always clear your own request to speak.
* You are able to set `request_to_speak_timestamp` to any present or future time.
## Modify User Voice State
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/voice-states/[\{user.id}](/developers/resources/user#user-object)
Updates another user's voice state. Returns `204 No Content` on success. Fires a [Voice State Update](/developers/events/gateway-events#voice-state-update) Gateway event.
###### JSON Params
| Field | Type | Description |
| ------------ | --------- | ---------------------------------------------- |
| channel\_id? | snowflake | the id of the channel the user is currently in |
| suppress? | boolean | toggles the user's suppress state |
###### Caveats
There are currently several caveats for this endpoint:
* `channel_id` must currently point to a stage channel.
* User must already have joined `channel_id`.
* You must have the `MUTE_MEMBERS` permission. (Since suppression is the only thing that is available currently.)
* When unsuppressed, non-bot users will have their `request_to_speak_timestamp` set to the current time. Bot users will not.
* When suppressed, the user will have their `request_to_speak_timestamp` removed.
# Webhook Resource
Source: https://docs.discord.com/developers/resources/webhook
Reference for Discord webhook objects and management endpoints.
Webhooks are a low-effort way to post messages to channels in Discord. They do not require a bot user or authentication to use.
Apps can also subscribe to webhook events (i.e. outgoing webhooks) when events happen *in* Discord, which is detailed in the [Webhook Events](/developers/events/webhook-events) documentation.
### Webhook Object
Used to represent a webhook.
###### Webhook Structure
| Field | Type | Description |
| ------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| id | snowflake | the id of the webhook |
| type | integer | the [type](/developers/resources/webhook#webhook-object-webhook-types) of the webhook |
| guild\_id? | ?snowflake | the guild id this webhook is for, if any |
| channel\_id | ?snowflake | the channel id this webhook is for, if any |
| user? | [user](/developers/resources/user#user-object) object | the user this webhook was created by (not returned when getting a webhook with its token) |
| name | ?string | the default name of the webhook |
| avatar | ?string | the default user avatar [hash](/developers/reference#image-formatting) of the webhook |
| token? | string | the secure token of the webhook (returned for Incoming Webhooks) |
| application\_id | ?snowflake | the bot/OAuth2 application that created this webhook |
| source\_guild? \* | partial [guild](/developers/resources/guild#guild-object) object | the guild of the channel that this webhook is following (returned for Channel Follower Webhooks) |
| source\_channel? \* | partial [channel](/developers/resources/channel#channel-object) object | the channel that this webhook is following (returned for Channel Follower Webhooks) |
| url? | string | the url used for executing the webhook (returned by the [webhooks](/developers/topics/oauth2#webhooks) OAuth2 flow) |
\* These fields will be absent if the webhook creator has since lost access to the guild where the followed channel resides
###### Webhook Types
These types don't include [webhook events](/developers/events/webhook-events), which are outgoing webhooks sent to your app by Discord. See [Webhook Events](/developers/events/webhook-events) for details.
| Value | Name | Description |
| ----- | ---------------- | -------------------------------------------------------------------------------------------------------------- |
| 1 | Incoming | Incoming Webhooks can post messages to channels with a generated token |
| 2 | Channel Follower | Channel Follower Webhooks are internal webhooks used with Channel Following to post new messages into channels |
| 3 | Application | Application webhooks are webhooks used with Interactions |
###### Example Incoming Webhook
```json theme={"system"}
{
"name": "test webhook",
"type": 1,
"channel_id": "199737254929760256",
"token": "3d89bb7572e0fb30d8128367b3b1b44fecd1726de135cbe28a41f8b2f777c372ba2939e72279b94526ff5d1bd4358d65cf11",
"avatar": null,
"guild_id": "199737254929760256",
"id": "223704706495545344",
"application_id": null,
"user": {
"username": "test",
"discriminator": "7479",
"id": "190320984123768832",
"avatar": "b004ec1740a63ca06ae2e14c5cee11f3",
"public_flags": 131328
}
}
```
###### Example Channel Follower Webhook
```json theme={"system"}
{
"type": 2,
"id": "752831914402115456",
"name": "Guildy name",
"avatar": "bb71f469c158984e265093a81b3397fb",
"channel_id": "561885260615255432",
"guild_id": "56188498421443265",
"application_id": null,
"source_guild": {
"id": "56188498421476534",
"name": "Guildy name",
"icon": "bb71f469c158984e265093a81b3397fb"
},
"source_channel": {
"id": "5618852344134324",
"name": "announcements"
},
"user": {
"username": "test",
"discriminator": "7479",
"id": "190320984123768832",
"avatar": "b004ec1740a63ca06ae2e14c5cee11f3",
"public_flags": 131328
}
}
```
###### Example Application Webhook
```json theme={"system"}
{
"type": 3,
"id": "658822586720976555",
"name": "Clyde",
"avatar": "689161dc90ac261d00f1608694ac6bfd",
"channel_id": null,
"guild_id": null,
"application_id": "658822586720976555"
}
```
## Create Webhook
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/webhooks
Creates a new webhook and returns a [webhook](/developers/resources/webhook#webhook-object) object on success. Requires the `MANAGE_WEBHOOKS` permission. Fires a [Webhooks Update](/developers/events/gateway-events#webhooks-update) Gateway event.
An error will be returned if a webhook name (`name`) is not valid. A webhook name is valid if:
* It does not contain the substrings `clyde` or `discord` (case-insensitive)
* It follows the nickname guidelines in the [Usernames and Nicknames](/developers/resources/user#usernames-and-nicknames) documentation, with an exception that webhook names can be up to 80 characters
This endpoint supports the `X-Audit-Log-Reason` header.
###### JSON Params
| Field | Type | Description |
| ------- | ----------------------------------------------- | ------------------------------------- |
| name | string | name of the webhook (1-80 characters) |
| avatar? | ?[image data](/developers/reference#image-data) | image for the default webhook avatar |
## Get Channel Webhooks
/channels/[\{channel.id}](/developers/resources/channel#channel-object)/webhooks
Returns a list of channel [webhook](/developers/resources/webhook#webhook-object) objects. Requires the `MANAGE_WEBHOOKS` permission.
## Get Guild Webhooks
/guilds/[\{guild.id}](/developers/resources/guild#guild-object)/webhooks
Returns a list of guild [webhook](/developers/resources/webhook#webhook-object) objects. Requires the `MANAGE_WEBHOOKS` permission.
## Get Webhook
/webhooks/[\{webhook.id}](/developers/resources/webhook#webhook-object)
Returns the new [webhook](/developers/resources/webhook#webhook-object) object for the given id.
This request requires the `MANAGE_WEBHOOKS` permission unless the application making the request owns the
webhook. [(see: webhook.application\_id)](/developers/resources/webhook#webhook-object)
## Get Webhook with Token
/webhooks/[\{webhook.id}](/developers/resources/webhook#webhook-object)/[\{webhook.token}](/developers/resources/webhook#webhook-object)
Same as above, except this call does not require authentication and returns no user in the webhook object.
## Modify Webhook
/webhooks/[\{webhook.id}](/developers/resources/webhook#webhook-object)
Modify a webhook. Requires the `MANAGE_WEBHOOKS` permission. Returns the updated [webhook](/developers/resources/webhook#webhook-object) object on success. Fires a [Webhooks Update](/developers/events/gateway-events#webhooks-update) Gateway event.
All parameters to this endpoint are optional.
This endpoint supports the `X-Audit-Log-Reason` header.
###### JSON Params
| Field | Type | Description |
| ----------- | ----------------------------------------------- | -------------------------------------------------- |
| name | string | the default name of the webhook |
| avatar | ?[image data](/developers/reference#image-data) | image for the default webhook avatar |
| channel\_id | snowflake | the new channel id this webhook should be moved to |
## Modify Webhook with Token
/webhooks/[\{webhook.id}](/developers/resources/webhook#webhook-object)/[\{webhook.token}](/developers/resources/webhook#webhook-object)
Same as above, except this call does not require authentication, does not accept a `channel_id` parameter in the body, and does not return a user in the webhook object.
## Delete Webhook
/webhooks/[\{webhook.id}](/developers/resources/webhook#webhook-object)
Delete a webhook permanently. Requires the `MANAGE_WEBHOOKS` permission. Returns a `204 No Content` response on success. Fires a [Webhooks Update](/developers/events/gateway-events#webhooks-update) Gateway event.
This endpoint supports the `X-Audit-Log-Reason` header.
## Delete Webhook with Token
/webhooks/[\{webhook.id}](/developers/resources/webhook#webhook-object)/[\{webhook.token}](/developers/resources/webhook#webhook-object)
Same as above, except this call does not require authentication.
## Execute Webhook
/webhooks/[\{webhook.id}](/developers/resources/webhook#webhook-object)/[\{webhook.token}](/developers/resources/webhook#webhook-object)
Refer to [Uploading Files](/developers/reference#uploading-files) for details on attachments and `multipart/form-data` requests. Returns a message or `204 No Content` depending on the `wait` query parameter.
Note that when sending a message, you must provide a value for at **least one of** `content`, `embeds`, `components`, `file`, or `poll`.
If the webhook channel is a forum or media channel, you must provide either `thread_id` in the query string params, or `thread_name` in the JSON/form params. If `thread_id` is provided, the message will send in that thread. If `thread_name` is provided, a thread with that name will be created in the channel.
Discord may strip certain characters from message content, like invalid unicode characters or characters which cause unexpected message formatting. If you are passing user-generated strings into message content, consider sanitizing the data to prevent unexpected behavior and using `allowed_mentions` to prevent unexpected mentions.
###### Query String Params
| Field | Type | Description | Required |
| ---------------- | ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| wait | [boolean](/developers/reference#boolean-query-strings) | waits for server confirmation of message send before response, and returns the created message body (defaults to `false`; when `false` a message that is not saved does not return an error) | false |
| thread\_id | snowflake | Send a message to the specified thread within a webhook's channel. The thread will automatically be unarchived. | false |
| with\_components | [boolean](/developers/reference#boolean-query-strings) | whether to respect the `components` field of the request. When enabled, allows application-owned webhooks to use all components and non-owned webhooks to use non-interactive components. (defaults to `false`) | false |
###### JSON/Form Params
| Field | Type | Description | Required |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- |
| content | string | the message contents (up to 2000 characters) | one of content, file, embeds, poll |
| username | string | override the default username of the webhook | false |
| avatar\_url | string | override the default avatar of the webhook | false |
| tts | boolean | true if this is a TTS message | false |
| embeds | array of up to 10 [embed](/developers/resources/message#embed-object) objects | embedded `rich` content | one of content, file, embeds, poll |
| allowed\_mentions | [allowed mention object](/developers/resources/message#allowed-mentions-object) | allowed mentions for the message | false |
| components \* | array of [message component](/developers/components/reference#component-object) | the components to include with the message | false |
| files\[n] \*\* | file contents | the contents of the file being sent | one of content, file, embeds, poll |
| payload\_json \*\* | string | JSON encoded body of non-file params | `multipart/form-data` only |
| attachments \*\* | array of partial [attachment request](/developers/resources/message#attachment-object-attachment-request-structure) objects | metadata for the attachments | false |
| flags \*\*\* | integer | [message flags](/developers/resources/message#message-object-message-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) (only `SUPPRESS_EMBEDS`, `SUPPRESS_NOTIFICATIONS` and `IS_COMPONENTS_V2` can be set) | false |
| thread\_name | string | name of thread to create (requires the webhook channel to be a forum or media channel) | false |
| applied\_tags | array of snowflakes | array of tag ids to apply to the thread (requires the webhook channel to be a forum or media channel) | false |
| poll | [poll](/developers/resources/poll#poll-create-request-object) request object | A poll! | one of content, file, embeds, poll |
\* Application-owned webhooks can always send components. Non-application-owned webhooks cannot send interactive components, and the `components` field will be ignored unless they set the `with_components` query param.
\*\* See [Uploading Files](/developers/reference#uploading-files) for details.
\*\*\* When the flag `IS_COMPONENTS_V2` is set, the webhook message can only contain `components`. Providing `content`, `embeds`, `files[n]` or `poll` will fail with a 400 BAD REQUEST response.
For the webhook embed objects, you can set every field except `type` (it will be `rich` regardless of if you try to set it), `provider`, `video`, and any `height`, `width`, or `proxy_url` values for images.
## Execute Slack-Compatible Webhook
/webhooks/[\{webhook.id}](/developers/resources/webhook#webhook-object)/[\{webhook.token}](/developers/resources/webhook#webhook-object)/slack
Refer to [Slack's documentation](https://api.slack.com/incoming-webhooks) for more information. We do not support Slack's `channel`, `icon_emoji`, `mrkdwn`, or `mrkdwn_in` properties.
###### Query String Params
| Field | Type | Description | Required |
| ---------- | ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| thread\_id | snowflake | id of the thread to send the message in | false |
| wait | [boolean](/developers/reference#boolean-query-strings) | waits for server confirmation of message send before response (defaults to `true`; when `false` a message that is not saved does not return an error) | false |
## Execute GitHub-Compatible Webhook
/webhooks/[\{webhook.id}](/developers/resources/webhook#webhook-object)/[\{webhook.token}](/developers/resources/webhook#webhook-object)/github
[Add a new webhook](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) to your GitHub repo (in the repo's settings), and use this endpoint as the "Payload URL." You can choose what events your Discord channel receives by choosing the "Let me select individual events" option and selecting individual events for the new webhook you're configuring. The supported [events](https://docs.github.com/en/webhooks/webhook-events-and-payloads) are `commit_comment`, `create`, `delete`, `fork`, `issue_comment`, `issues`, `member`, `public`, `pull_request`, `pull_request_review`, `pull_request_review_comment`, `push`, `release`, `watch`, `check_run`, `check_suite`, `discussion`, and `discussion_comment`.
###### Query String Params
| Field | Type | Description | Required |
| ---------- | ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| thread\_id | snowflake | id of the thread to send the message in | false |
| wait | [boolean](/developers/reference#boolean-query-strings) | waits for server confirmation of message send before response (defaults to `true`; when `false` a message that is not saved does not return an error) | false |
## Get Webhook Message
/webhooks/[\{webhook.id}](/developers/resources/webhook#webhook-object)/[\{webhook.token}](/developers/resources/webhook#webhook-object)/messages/[\{message.id}](/developers/resources/message#message-object)
Returns a previously-sent webhook message from the same token. Returns a [message](/developers/resources/message#message-object) object on success.
###### Query String Params
| Field | Type | Description | Required |
| ---------- | --------- | ---------------------------------- | -------- |
| thread\_id | snowflake | id of the thread the message is in | false |
## Edit Webhook Message
/webhooks/[\{webhook.id}](/developers/resources/webhook#webhook-object)/[\{webhook.token}](/developers/resources/webhook#webhook-object)/messages/[\{message.id}](/developers/resources/message#message-object)
Edits a previously-sent webhook message from the same token. Returns a [message](/developers/resources/message#message-object) object on success.
When the `content` field is edited, the arrays `mentions` and `mention_roles` and the boolean `mention_everyone` in the message object will be reconstructed from scratch based on the new content. When the message flag `IS_COMPONENTS_V2` is set, the reconstructed arrays and boolean are based on the edited content in the `components` array. The `allowed_mentions` field of the edit request controls how this happens. If there is no explicit `allowed_mentions` in the edit request, the content will be parsed with *default* allowances, that is, without regard to whether or not an `allowed_mentions` was present in the request that originally created the message.
Refer to [Uploading Files](/developers/reference#uploading-files) for details on attachments and `multipart/form-data` requests.
Any provided files will be **appended** to the message. To remove or replace files you will have to supply the `attachments` field which specifies the files to retain on the message after edit.
Starting with API v10, the `attachments` array must contain all attachments that should be present after edit, including **retained and new** attachments provided in the request body.
All parameters to this endpoint are optional and nullable.
###### Query String Params
| Field | Type | Description | Required |
| ---------------- | ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| thread\_id | snowflake | id of the thread the message is in | false |
| with\_components | [boolean](/developers/reference#boolean-query-strings) | whether to respect the `components` field of the request. When enabled, allows application-owned webhooks to use all components and non-owned webhooks to use non-interactive components. (defaults to `false`) | false |
###### JSON/Form Params
| Field | Type | Description |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| content | string | the message contents (up to 2000 characters) |
| embeds | array of up to 10 [embed](/developers/resources/message#embed-object) objects | embedded `rich` content |
| flags \* | integer | [message flags](/developers/resources/message#message-object-message-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) (`SUPPRESS_EMBEDS` and `IS_COMPONENTS_V2` only) |
| allowed\_mentions | [allowed mention object](/developers/resources/message#allowed-mentions-object) | allowed mentions for the message |
| components \*\* | array of [message component](/developers/components/reference#component-object) | the components to include with the message |
| files\[n] \*\*\* | file contents | the contents of the file being sent/edited |
| payload\_json \*\*\* | string | JSON encoded body of non-file params (multipart/form-data only) |
| attachments \*\*\* | array of partial [attachment request](/developers/resources/message#attachment-object-attachment-request-structure) objects | attached files to keep and their metadata |
| poll \*\*\*\* | [poll](/developers/resources/poll#poll-create-request-object) request object | A poll! |
\* The `SUPPRESS_EMBEDS` flag can be both set and unset, while the `IS_COMPONENTS_V2` flag can only be set. When the `IS_COMPONENTS_V2` flag is set, any of the used `content`, `embeds`, `files[n]` or `poll` values in the initial message must be set to `null` first, otherwise it will fail with a 400 BAD REQUEST response.
\*\* Application-owned webhooks can always send components. Non-application-owned webhooks cannot send interactive components, and the `components` field will be ignored unless they set the `with_components` query param.
\*\*\* See [Uploading Files](/developers/reference#uploading-files) for details.
\*\*\*\* Polls can only be added when editing a deferred interaction response.
## Delete Webhook Message
/webhooks/[\{webhook.id}](/developers/resources/webhook#webhook-object)/[\{webhook.token}](/developers/resources/webhook#webhook-object)/messages/[\{message.id}](/developers/resources/message#message-object)
Deletes a message that was created by the webhook. Returns a `204 No Content` response on success.
###### Query String Params
| Field | Type | Description | Required |
| ---------- | --------- | ---------------------------------- | -------- |
| thread\_id | snowflake | id of the thread the message is in | false |
# Setting a User's Rich Presence
Source: https://docs.discord.com/developers/rich-presence/using-with-the-embedded-app-sdk
When developing an [Activity](/developers/activities/overview), the [Embedded App SDK](/developers/developer-tools/embedded-app-sdk) makes it easy to integrate Rich Presence to display details about what a user is up to inside of your game or social experience.
Rich Presence data can be thought of as an extension of your Activity—and leveling it up just a *little* makes it more interesting and relevant to the user playing your Activity (and their friends that might want to jump in and play). This guide provides an overview of the platform and technical knowledge you need to integrate Rich Presence with your existing Activity.
Not sure if you should be building with the Embedded App SDK? Read through [Choosing an SDK](/developers/rich-presence/overview#choosing-an-sdk) to understand your options when integrating Rich Presence with your app.
The rest of the guide assumes you've already developed an [app](/developers/quick-start/overview-of-apps) that can launch an Activity. If you aren't at that point quite yet, you should follow the guide on [building your first Activity](/developers/activities/building-an-activity) before continuing.
## Understanding Rich Presence Data
### Default Rich Presence Data
By default, when a user is connected to your Activity, the app's icon will appear on their profile. If the user viewing the profile has the ability to join, an "Ask to Join" button will be displayed as well.
While this is okay, it's pretty limited and doesn't provide much context about what a user is actually *doing* inside of the Activity. In the following sections, we'll take a look at what richer and more actionable presence can look like.
### Custom Rich Presence Data
Now let's see what custom presence data can look like when a user joins your Activity. The [types for these fields](/developers/rich-presence/using-with-the-embedded-app-sdk#setactivity-fields) and [examples](/developers/rich-presence/using-with-the-embedded-app-sdk#setactivity-example) are in the sections below, but for now let's just get an idea of what we're working with:
A few small things to note about the above image:
1. `large_image` and `small_image` are both in the `assets` object, which you can see below in the [table below](/developers/rich-presence/using-with-the-embedded-app-sdk#activity-partial-object). They're labeled with the object's keys to make it more clear how they appear in a Discord profile.
2. You can't set App Name when setting presence—it's always the name configured in your [app's settings](https://discord.com/developers/applications).
3. The state `(1 of max_party)` badge will only render when a party field is provided. Otherwise, state will be shown in a line of text below details.
## Updating Presence
When updating Rich Presence data using the Embedded App SDK, the only real command you need to use is **[`setActivity()`](/developers/developer-tools/embedded-app-sdk#setactivity)**. Under the hood, `setActivity()` calls the RPC [`SET_ACTIVITY` command](/developers/topics/rpc#setactivity) with the features and fields available when you're building an Activity.
As you start exploring the Rich Presence docs, you'll start seeing the word "activity" a *lot*. The "activities" referenced in docs (like the [RPC ones](/developers/topics/rpc#setactivity)) aren't related to the Activities you're building with the Embedded App SDK.
When Rich Presence was introduced, the underlying object that contains presence data was called an "activity" (long before the Embedded App SDK), which is what the RPC [`SET_ACTIVITY` command](/developers/topics/rpc#setactivity) is referencing. And that's *also* why the Embedded App SDK's wrapper around the RPC command is called `setActivity()` yet isn't really related to setting the state for the kind of Activity that *you're* building.
We know, it's confusing ¯\\\_(⊙︿⊙)\_/¯ — the naming was logical at the time because it was really about the user's activity in a 3rd party game or service, but now it sorta feels like activity-ception. Understanding the nuances here aren't super important, and it's why we have guides like this one. But as they say...the more you (at least sort of) know.
### rpc.activities.write Scope
To display custom Rich Presence data for a user, your app will need to be authorized with the [`rpc.activities.write` scope](/developers/topics/oauth2#shared-resources-oauth2-scopes) for that user.
To request the scope, your [`authorize()`](/developers/developer-tools/embedded-app-sdk#authorize) call might look something like this:
The following example only focuses on calling `authorize()`. Follow the [Building an Activity guide](/developers/activities/building-an-activity) for more details on topics like installing and instantiating the Embedded App SDK.
```js theme={"system"}
// Authorize with Discord Client
const { code } = await discordSdk.commands.authorize({
client_id: import.meta.env.VITE_DISCORD_CLIENT_ID,
response_type: "code",
state: "",
prompt: "none",
scope: [
"identify",
"rpc.activities.write"
],
});
```
### setActivity Fields
When calling [`setActivity()`](/developers/developer-tools/embedded-app-sdk#setactivity), you are expected to pass a partial [activity object](/developers/events/gateway-events#activity-object-activity-structure).
Below is a table with many of the available fields for the activity partial. Some were left out since they don't have an effect for Activities.
#### Activity partial object
All of the fields on the partial object are optional and nullable
| field | type | description |
| ---------- | ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| type | integer | [Activity](/developers/events/gateway-events#activity-object-activity-types) type, which determines the header text for the Rich Presence data |
| state | string | User's current party status |
| details | string | What the player is currently doing in your Activity |
| timestamps | [timestamps](/developers/events/gateway-events#activity-object-activity-timestamps) object | Unix timestamps to display start and/or end times |
| assets | [assets](/developers/events/gateway-events#activity-object-activity-assets) object | Images used for the Rich Presence data (and their hover texts) |
| party | [party](/developers/events/gateway-events#activity-object-activity-party) object | Information for the current party of the player |
### setActivity Example
Now let's take a look at more of a real example. Take a look at the Rich Presence data below that is for an Activity:
To create this sort of Rich Presence, here is what the `setActivity()` code would look like:
The following example only focuses on using `setActivity()`. Follow the [Building an Activity guide](/developers/activities/building-an-activity) for more details on topics like instantiating the Embedded App SDK and authenticating users.
```js theme={"system"}
await discordSdk.commands.setActivity({
activity: {
type: 0,
details: 'Traveling with a group',
state: 'In Mainframe',
assets: {
large_image: 'main-game-image',
large_text: 'in a group',
small_image: 'map-mainframe',
small_text: 'in mainframe'
},
timestamps: {
start: 1723137832
},
party: {
size: [2,4]
}
}
});
```
## Uploading Custom Assets
To upload custom art assets for Rich Presence, navigate to your [app's settings](https://discord.com/developers/applications) and click **Rich Presence** on the left-hand sidebar. On the [**Art Assets** page](https://discord.com/developers/applications/select/rich-presence/assets), you can upload up to 300 custom assets for your app. It's recommended to make all Rich Presence assets **1024 x 1024**.
When uploading assets, the asset keys will automatically be changed to lowercase. Keep this in mind when referencing asset keys in the `assets` object when calling [`setActivity()`](/developers/developer-tools/embedded-app-sdk#setactivity)—for example, an asset uploaded as `Main-Game-Image` would be referenced as `main-game-image`.
For tips on choosing assets, take a look at the [Rich Presence best practices guide](/developers/rich-presence/best-practices#have-interesting-expressive-art).
## Using External Custom Assets
Typically when building an Activity, you need to be aware of the proxy and [how to use external resources](/developers/activities/development-guides/networking#using-external-resources). However, luckily for you (and the writer of this guide), image URLs in fields for features like Rich Presence don't need to jump through any extra hoops.
If you have more than 300 custom assets or want to use images stored somewhere else, you can specify an external URL for `large_image` or `small_image` within the `assets` object.
The following example only focuses on using `setActivity()`. Follow the [Building an Activity guide](/developers/activities/building-an-activity) for more details on topics like instantiating the Embedded App SDK and authenticating users.
```js theme={"system"}
await discordSdk.commands.setActivity({
activity: {
type: 2,
state: 'Broken Hearts and Code (club edit)',
details: 'DJ Wump',
assets: {
large_image: 'https://example.com/album-covers/dj-wump/broken-code-and-hearts-club-edit.jpg',
large_text: 'Listening to a track',
}
}
});
```
Uploaded assets (added via the developer portal) support PNG, JPEG, and WebP only — animated images are not supported for uploaded assets.
Unlike uploaded assets, external URLs also support GIF, animated WebP, and AVIF. See [Activity Asset Image](/developers/events/gateway-events#activity-object-activity-asset-image) for details.
# Social Commerce & Game Shops
Source: https://docs.discord.com/developers/social-commerce/overview
Sell in-game items, cosmetics, and bundles directly within Discord through a user-facing Game Shop that lives directly in your server.
Discord Social Commerce brings your game's store directly into Discord, where your players already are. Instead of sending players to an external store, Game Shops let them browse items, make purchases, and send gifts to friends without ever leaving Discord.
## Discord Game Shops
Your Game Shop lives inside your Discord server and surfaces items across Discord: on player profiles, in DMs, and in voice chats. Players discover items organically through social activity, not just because they went looking for a store.
When a player makes a purchase, Discord handles payment processing and creates an [entitlement](/developers/resources/entitlement) representing that the player is owed the item. Your backend listens for the fulfillment webhook, checks that their Discord account is linked to their game account, and grants the item.
## Gifting & Wishlists
Game Shops also unlock a category of purchaser that traditional in-game stores can't reach: people who don't play your game but want to buy something for someone who does.
A player's friends, family members, or community followers can browse your Game Shop, pick an item off a wishlist, and send it as a gift. They don't need to own or ever launch your game to do it. For you as a developer, every player in your Discord community becomes a potential storefront for your items, and the people in their social circles become a new pool of buyers.
Wishlists make this loop even tighter. Players pin the items they want, making it easy for gift-givers to find something meaningful rather than guessing. Birthdays, game launches, seasonal events: any moment that drives gifting behavior on Discord becomes a revenue opportunity for your shop.
## Social Commerce APIs
Social Commerce is backed by a set of APIs that give you full control over your catalog, purchases, and fulfillment:
* **SKU & Storefront Management:** Define your catalog of items including cosmetics, bundles, and consumables. Set pricing, availability, and metadata through the API so your store stays in sync with your game.
* **Entitlements:** Query and manage what players own. When a purchase is made, Discord creates an entitlement your backend can verify and act on.
* **Fulfillment:** Receive webhook events the moment a purchase completes. Grant the item in your game, then mark the entitlement as fulfilled via the API to close the loop.
Together, these APIs let you build a fully automated purchase-to-fulfillment pipeline with Discord handling the payment surface and your backend handling item delivery.
## Get Access to Social Commerce
Social Commerce is currently in early access. If you're interested in bringing your Game Shop to Discord, get in touch and we'll follow up as we expand access.
}>
Fill out our interest form and we'll reach out when we're ready to onboard new studios.
# Certified Devices
Source: https://docs.discord.com/developers/topics/certified-devices
Learn about Discord's Certified Device program for hardware integration.
Baked into Discord is the ability for hardware manufacturers to tell us a little more about the certified devices that are plugged into a user's computer. Unfortunately, no, you can't show that a user's PUBG Chicken Dinner was all thanks to the amazing TotallyRealHardware RGB Mouse and Keyboard Set Extraordinaire™, but you *can* give them an amazing experience using your hardware with Discord!
## How's it work?
I'm glad you asked!
1. [Create an application](https://discord.com/developers/applications) for your hardware vendor—save the Client ID!
2. Talk to Discord via one simple HTTP or WebSocket call
3. Send us a [`SET_CERTIFIED_DEVICES`](/developers/topics/rpc#setcertifieddevices) WebSocket payload or equivalent HTTP POST whenever the state of the device changes
Yup, that's it. You give us the real-time info about any connected devices, and we'll handle the rest to make sure that anyone using your device will have an awesome experience. Your device will also have a `CERTIFIED` badge in Discord's audio settings, and really, who doesn't love badges?
## Connecting
###### Query String Params
| Name | Value | Required |
| ---------- | -------------------- | --------- |
| v | `1` | All |
| client\_id | your app's client id | All |
| encoding | `json` | WebSocket |
You can send event updates to the following URIs:
###### HTTP
```
http://127.0.0.1:PORT/rpc?v=1&client_id=YOUR_CLIENT_ID
```
###### WebSocket
```
ws://127.0.0.1:PORT?v=1&client_id=YOUR_CLIENT_ID&encoding=json
```
`PORT` is a range of ports from `6463` to `6473`. You should iterate over these ports with your request until one returns a success response code or succeeds with a socket connection. Keep track of that port number for the rest of the session.
To keep your hardware in sync with Discord, send updates any time the hardware mute is toggled, or one of the voice features like echo cancellation is enabled or disabled by the user. This lets Discord get out of the way of your optimization when you're in control, or help out when you're not, ensuring an awesome experience for anyone using your hardware.
Each time you update, send a full array of `devices`, sorted by your preferred priority. That means if you want a specific headset to be the default that Discord will attempt to use, put it first in the array.
## Getting Device UUID
For each device in the `SET_CERTIFIED_DEVICES` payload, there is an `id` field. This `id` should be the Windows device's UUID, retrieved through the native Windows API. You'll get back something that looks like `{0.0.1.00000000}.{6cff2b76-44a8-46b9-b528-262ad8609d9f}`.
On macOS, the `id` will be the name of the device.
###### Microphone Id Example
```cpp theme={"system"}
id = waveInMessage((HWAVEIN)IntToPtr(index),
DRV_QUERYFUNCTIONINSTANCEID,
(DWORD_PTR)pstrEndpointId,
cbEndpointId);
```
###### Speaker Id Example
```cpp theme={"system"}
id = waveOutMessage((HWAVEIN)IntToPtr(index),
DRV_QUERYFUNCTIONINSTANCEID,
(DWORD_PTR)pstrEndpointId,
cbEndpointId);
```
## HTTP Example
###### HTTP Request Example
```bash theme={"system"}
curl -X POST -H 'Content-Type: application/json' -d '
{
"nonce": "9b4e9711-97f3-4f35-b047-32c82a51978e",
"cmd": "SET_CERTIFIED_DEVICES",
"args": {
"devices": [
{
"type": "audioinput",
"id": "{0.0.1.00000000}.{6cff2b76-44a8-46b9-b528-262ad8609d9f}",
"vendor": {
"name": "SteelSeries",
"url": "https://steelseries.com"
},
"model": {
"name": "Arctis 7",
"url": "https://steelseries.com/gaming-headsets/arctis-7"
},
"related": ["{0.0.1.00000000}.{6cff2b76-44a8-46b9-b528-262ad8609d9f}"],
"echo_cancellation": true,
"noise_suppression": true,
"automatic_gain_control": true,
"hardware_mute": false
}
]
}
}
' http://127.0.0.1:PORT/rpc?v=1&client_id=YOUR_CLIENT_ID
```
The socket will respond with a `200 OK` status code and the following JSON.
###### HTTP Response Example
```json theme={"system"}
{
"cmd": "SET_CERTIFIED_DEVICES",
"data": null,
"evt": null,
"nonce": "9b4e9711-97f3-4f35-b047-32c82a51978e"
}
```
## WebSocket Example
###### RPC Command Example
```json theme={"system"}
{
"nonce": "9b4e9711-97f3-4f35-b047-32c82a51978e",
"cmd": "SET_CERTIFIED_DEVICES",
"args": {
"devices": [
{
"type": "audioinput",
"id": "{0.0.1.00000000}.{6cff2b76-44a8-46b9-b528-262ad8609d9f}",
"vendor": {
"name": "SteelSeries",
"url": "https://steelseries.com"
},
"model": {
"name": "Arctis 7",
"url": "https://steelseries.com/gaming-headsets/arctis-7"
},
"related": ["{0.0.1.00000000}.{6cff2b76-44a8-46b9-b528-262ad8609d9f}"],
"echo_cancellation": true,
"noise_suppression": true,
"automatic_gain_control": true,
"hardware_mute": false
}
]
}
}
```
###### RPC Response Example
```json theme={"system"}
{
"nonce": "9b4e9711-97f3-4f35-b047-32c82a51978e",
"cmd": "SET_CERTIFIED_DEVICES",
"data": null,
"evt": null
}
```
## Models
###### Device Object
| Field | Type | Description |
| --------------------------- | -------------------------------------------------------------------------- | -------------------------------------------------------- |
| type | [device type](/developers/topics/certified-devices#models-device-type) | the type of device |
| id | string | the device's Windows UUID |
| vendor | [vendor](/developers/topics/certified-devices#models-vendor-object) object | the hardware vendor |
| model | [model](/developers/topics/certified-devices#models-model-object) object | the model of the product |
| related | array of strings | UUIDs of related devices |
| echo\_cancellation?\* | boolean | if the device's native echo cancellation is enabled |
| noise\_suppression?\* | boolean | if the device's native noise suppression is enabled |
| automatic\_gain\_control?\* | boolean | if the device's native automatic gain control is enabled |
| hardware\_mute?\* | boolean | if the device is hardware muted |
\*These fields are only applicable for `AUDIO_INPUT` device types
###### Vendor Object
| Field | Type | Description |
| ----- | ------ | ------------------ |
| name | string | name of the vendor |
| url | string | url for the vendor |
###### Model Object
| Field | Type | Description |
| ----- | ------ | ----------------- |
| name | string | name of the model |
| url | string | url for the model |
###### Device Type
| Type | Value |
| ------------- | ------------- |
| AUDIO\_INPUT | "audioinput" |
| AUDIO\_OUTPUT | "audiooutput" |
| VIDEO\_INPUT | "videoinput" |
# OAuth2
Source: https://docs.discord.com/developers/topics/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. We support the authorization code grant, the implicit 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](https://discord.com/developers/applications) 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](https://tools.ietf.org/html/rfc6749) 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
| URL | Description |
| ------------------------------------------------------------------------------------------ | ----------------------------------------------------------- |
| [https://discord.com/oauth2/authorize](https://discord.com/oauth2/authorize) | Base authorization URL |
| [https://discord.com/api/oauth2/token](https://discord.com/api/oauth2/token) | Token URL |
| [https://discord.com/api/oauth2/token/revoke](https://discord.com/api/oauth2/token/revoke) | [Token Revocation](https://tools.ietf.org/html/rfc7009) URL |
In accordance with the relevant RFCs, the token and token revocation URLs will **only** accept a content type of `application/x-www-form-urlencoded`. JSON content is not permitted and will return an error.
###### OAuth2 Scopes
These are a list of all the OAuth2 scopes that Discord supports. Some scopes require approval from Discord to use. Requesting them from a user without approval from Discord may cause errors or undocumented behavior in the OAuth2 flow.
To inquire about access about scopes that are only available to approved partners, speak to your Discord account
representative to see if they are a fit for your needs.
| Name | Description |
| ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| activities.read | allows your app to fetch data from a user's "Now Playing/Recently Played" list — not currently available for apps |
| activities.write | allows your app to update a user's activity - not currently available for apps (NOT REQUIRED FOR [GAMESDK ACTIVITY MANAGER](/developers/developer-tools/game-sdk#activities)) |
| applications.builds.read | allows your app to read build data for a user's applications |
| applications.builds.upload | allows your app to upload/update builds for a user's applications - only available to approved partners |
| applications.commands | allows your app to add [commands](/developers/interactions/application-commands) to a guild - included by default with the `bot` scope |
| applications.commands.update | allows your app to update its [commands](/developers/interactions/application-commands) using a Bearer token - [client credentials grant](/developers/topics/oauth2#client-credentials-grant) only |
| applications.commands.permissions.update | allows your app to update [permissions for its commands](/developers/interactions/application-commands#permissions) in a guild a user has permissions to |
| applications.entitlements | allows your app to read entitlements for a user's applications |
| applications.store.update | allows your app to read and update store data (SKUs, store listings, achievements, etc.) for a user's applications |
| bot | for oauth2 bots, this puts the bot in the user's selected guild by default |
| connections | allows [`/users/@me/connections`](/developers/resources/user#get-current-user-connections) to return linked third-party accounts |
| dm\_channels.read | allows your app to see information about the user's DMs and group DMs - only available to approved partners |
| email | enables [`/users/@me`](/developers/resources/user#get-current-user) to return an `email` |
| gdm.join | allows your app to [join users to a group dm](/developers/resources/channel#group-dm-add-recipient) |
| guilds | allows [`/users/@me/guilds`](/developers/resources/user#get-current-user-guilds) to return basic information about all of a user's guilds |
| guilds.join | allows [`/guilds/{guild.id}/members/{user.id}`](/developers/resources/guild#add-guild-member) to be used for joining users to a guild |
| guilds.members.read | allows [`/users/@me/guilds/{guild.id}/member`](/developers/resources/user#get-current-user-guild-member) to return a user's member information in a guild |
| identify | allows [`/users/@me`](/developers/resources/user#get-current-user) without `email` |
| identify.premium | allows your app to read a user's Nitro subscription type as defined by `premium_type` on the [User object](/developers/resources/user#user-object-user-structure) - only available to approved partners |
| messages.read | for local rpc server api access, this allows you to read messages from all client channels (otherwise restricted to channels/guilds your app creates) |
| relationships.read | Allows your app to access a user’s Discord Friends list, their pending requests, and blocked users. This scope is part of our Social SDK - [submit for access here](https://discord.com/developers/applications/select/social-sdk/getting-started). [Social SDK Terms apply](https://support-dev.discord.com/hc/en-us/articles/30225844245271-Discord-Social-SDK-Terms), including Section 5(a)(ii) to the data you obtain |
| role\_connections.write | allows your app to update a user's connection and metadata for the app |
| rpc | for local rpc server access, this allows you to control a user's local Discord client - only available to approved partners |
| rpc.activities.write | for local rpc server access, this allows you to update a user's activity - only available to approved partners |
| rpc.notifications.read | for local rpc server access, this allows you to receive notifications pushed out to the user - only available to approved partners |
| rpc.voice.read | for local rpc server access, this allows you to read a user's voice settings and listen for voice events - only available to approved partners |
| rpc.voice.write | for local rpc server access, this allows you to update a user's voice settings - only available to approved partners |
| voice | allows your app to connect to voice on user's behalf and see all the voice members - only available to approved partners |
| webhook.incoming | this generates a webhook that is returned in the oauth token response for authorization code grants |
In order to add a user to a guild, your bot has to already belong to that guild. `role_connections.write` cannot be used with the [Implicit grant type](/developers/topics/oauth2#implicit-grant).
## 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](https://en.wikipedia.org/wiki/Cross-site_request_forgery), or CSRF, and [Clickjacking](https://en.wikipedia.org/wiki/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](https://en.wikipedia.org/wiki/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.
While Discord does not require the use of the `state` parameter, we support it and 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.
All calls to the OAuth2 endpoints require either HTTP Basic authentication or `client_id` and `client_secret` supplied in the form data body.
###### Authorization URL Example
```
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
```
`client_id` is your application's `client_id`. `scope` is a list of [OAuth2 scopes](/developers/topics/oauth2#shared-resources-oauth2-scopes) separated by url encoded spaces (`%20`). `redirect_uri` is whatever URL you registered when creating your application, url-encoded. `state` is the unique string mentioned in [State and Security](/developers/topics/oauth2#state-and-security).
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 querystring 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](/developers/resources/application#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 0 (GUILD\_INSTALL) the application will be authorized for installation to a server, and when set to 1 (USER\_INSTALL) the application will be authorized for installation to a user. The application must be configured in the Developer Portal to support the provided `integration_type`.
###### Redirect URL Example
```
https://nicememe.website/?code=NhhvTDYsFcdgNLnnLijcl7Ku7bEEeee&state=15773059ghq9183habn
```
`code` is now exchanged for the user's access token by making a `POST` request to the [token URL](/developers/topics/oauth2#shared-resources-oauth2-urls) with the following parameters:
* `grant_type` - must be set to `authorization_code`
* `code` - the code from the querystring
* `redirect_uri` - the `redirect_uri` associated with this authorization, usually from your authorization URL
###### Access Token Exchange Example
```python theme={"system"}
import requests
API_ENDPOINT = 'https://discord.com/api/v10'
CLIENT_ID = '332269999912132097'
CLIENT_SECRET = '937it3ow87i4ery69876wqire'
REDIRECT_URI = 'https://nicememe.website'
def exchange_code(code):
data = {
'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, auth=(CLIENT_ID, CLIENT_SECRET))
r.raise_for_status()
return r.json()
```
In response, you will receive:
###### Access Token Response
```json theme={"system"}
{
"access_token": "6qrZcUqja7812RVdnEKjpzOL4CvHBFG",
"token_type": "Bearer",
"expires_in": 604800,
"refresh_token": "D43f5y0ahjqew82jZ4NViEr2YafMKhue",
"scope": "identify"
}
```
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. `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 `POST` request to the [token URL](/developers/topics/oauth2#shared-resources-oauth2-urls) with the following parameters:
* `grant_type` - must be set to `refresh_token`
* `refresh_token` - the user's refresh token
###### Refresh Token Exchange Example
```python theme={"system"}
import requests
API_ENDPOINT = 'https://discord.com/api/v10'
CLIENT_ID = '332269999912132097'
CLIENT_SECRET = '937it3ow87i4ery69876wqire'
def refresh_token(refresh_token):
data = {
'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, auth=(CLIENT_ID, CLIENT_SECRET))
r.raise_for_status()
return r.json()
```
Boom; fresh [access token response](/developers/topics/oauth2#authorization-code-grant-access-token-response)!
###### Token Revocation Example
To disable an access or refresh token, you can revoke it by making a `POST` request to the [token revocation URL](/developers/topics/oauth2#shared-resources-oauth2-urls) with the following parameters:
* `token` - the access token or refresh token to revoke
* `token_type_hint` *(optional)* - the `token` parameter's type—either `access_token` or `refresh_token`
When you revoke a token, any active access or refresh tokens associated with that authorization will be revoked, regardless of the `token` and `token_type_hint` values you pass in.
```python theme={"system"}
import requests
API_ENDPOINT = 'https://discord.com/api/v10'
CLIENT_ID = '332269999912132097'
CLIENT_SECRET = '937it3ow87i4ery69876wqire'
def revoke_access_token(access_token):
data = {
'token': access_token,
'token_type_hint': 'access_token'
}
headers = {
'Content-Type': 'application/x-www-form-urlencoded'
}
requests.post('%s/oauth2/token/revoke' % API_ENDPOINT, data=data, headers=headers, auth=(CLIENT_ID, CLIENT_SECRET))
```
Boom; the tokens are safely revoked.
## 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:
###### Authorization URL Example
```
https://discord.com/oauth2/authorize?response_type=token&client_id=290926444748734499&state=15773059ghq9183habn&scope=identify
```
On redirect, your redirect URI will contain additional **URI fragments**: `access_token`, `token_type`, `expires_in`, `scope`, and [`state`](/developers/topics/oauth2#state-and-security)(if specified). **These are not querystring parameters.** Be mindful of the "#" character:
###### Redirect URL Example
```
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 re-authorize 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 `POST` request to the [token URL](/developers/topics/oauth2#shared-resources-oauth2-urls) with a grant type of `client_credentials`, using Basic authentication with your client id as the username and your client secret as the password, 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](/developers/topics/oauth2#shared-resources-oauth2-scopes) separated by spaces:
Team applications are limited to the `identify` and `applications.commands.update` scope, because teams are not bound to a specific user.
###### Client Credentials Token Request Example
```python theme={"system"}
import requests
API_ENDPOINT = 'https://discord.com/api/v10'
CLIENT_ID = '332269999912132097'
CLIENT_SECRET = '937it3ow87i4ery69876wqire'
def get_token():
data = {
'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, auth=(CLIENT_ID, CLIENT_SECRET))
r.raise_for_status()
return r.json()
```
In return, you will receive an access token (without a refresh token):
###### Client Credentials Access Token Response
```json theme={"system"}
{
"access_token": "6qrZcUqja7812RVdnEKjpzOL4CvHBFG",
"token_type": "Bearer",
"expires_in": 604800,
"scope": "identify connections"
}
```
## Bot Users
Discord's API provides bot users, which are a separate type of user dedicated to automation. Bot users are automatically added to all apps, and are authenticated using the bot token found in your [app's settings](https://discord.com/developers/applications). Unlike the normal OAuth2 flow, bot users have full access to most API routes without using bearer tokens, and can connect to the [Real Time Gateway](/developers/events/gateway).
### Bot vs User Accounts
Developers must abide by the [terms of service](https://support-dev.discord.com/hc/articles/8562894815383-Discord-Developer-Terms-of-Service), which includes refraining from automating standard user accounts (generally called "self-bots") outside of the OAuth2/bot API.
Bot users have a few differences compared to standard Discord users:
1. Bots are added to guilds through the OAuth2 API, and cannot accept normal invites.
2. Bots cannot have friends or be added to or join Group DMs.
3. [Verified bots](https://support-dev.discord.com/hc/en-us/articles/23926564536471-How-Do-I-Get-My-App-Verified) do not have a maximum number of guilds.
4. Bots have an entirely separate set of [rate limits](/developers/topics/rate-limits#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:
###### Bot Auth Parameters
| name | description |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| client\_id | your app's client id |
| scope? | needs to include `bot` for the bot flow |
| permissions? | the [permissions](/developers/topics/permissions) you're requesting |
| guild\_id? | pre-fills the dropdown picker with a guild for the user |
| disable\_guild\_select? | `true` or `false`—disallows the user from changing the guild dropdown |
| integration\_type? | the [installation context](/developers/resources/application#application-object-application-integration-types) for the authorization |
###### URL Example
```
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](/developers/topics/permissions#permissions-bitwise-permission-flags) 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 permission 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 you only provide the `client_id` parameter (with optionally `permissions`, `guild_id`, or `disable_guild_select`), the authorization will use the [default install settings](/developers/resources/application#configuring-an-install-link-and-default-install-settings) configured in the developer portal. Specifying `scope`, `integration_type`, or `redirect_uri` will override this behavior.
If your bot is super specific to your private clubhouse, or you just don't like sharing, you can leave the `Public Bot` option unchecked in your application's settings. If unchecked, only you can add the bot to guilds. If marked as public, anyone with your bot's URL 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` and `applications.commands`, which will prompt a continuation into a complete [authorization code grant flow](/developers/topics/oauth2#authorization-code-grant) and add the ability to request the user's access token. If you request any scopes outside of `bot` and `applications.commands`, `response_type` is again mandatory; we will also automatically redirect the user to the first URI in your application's registered list unless `redirect_uri` is specified.
When receiving the access code on redirect, there will be additional querystring parameters of `guild_id` and `permissions`. The `guild_id` parameter should only be used as a hint as to the relationship between your bot and a guild. To be sure of the relationship between your bot and the guild, consider requiring the Oauth2 code grant in your bot's settings. Enabling it requires anyone adding your bot to a server to go through a full OAuth2 [authorization code grant flow](/developers/topics/oauth2#authorization-code-grant). When you retrieve the user's access token, you'll also receive information about the guild to which your bot was added:
###### Extended Bot Authorization Access Token Example
```json theme={"system"}
{
"token_type": "Bearer",
"guild": {
"mfa_level": 0,
"emojis": [],
"application_id": null,
"name": "SomeTest",
"roles": [
{
"hoist": false,
"name": "@everyone",
"mentionable": false,
"color": 0,
"position": 0,
"id": "290926798626357250",
"managed": false,
"permissions": 49794241,
"permissions_new": "49794241"
}
],
"afk_timeout": 300,
"system_channel_id": null,
"widget_channel_id": null,
"region": "us-east",
"default_message_notifications": 1,
"explicit_content_filter": 0,
"splash": null,
"features": [],
"afk_channel_id": null,
"widget_enabled": false,
"verification_level": 0,
"owner_id": "53908232999183680",
"id": "2909267986347357250",
"icon": null,
"description": null,
"public_updates_channel_id": null,
"safety_alerts_channel_id": null,
"rules_channel_id": null,
"max_members": 100000,
"vanity_url_code": null,
"premium_subscription_count": 0,
"premium_tier": 0,
"preferred_locale": "en-US",
"system_channel_flags": 0,
"banner": null,
"max_presences": null,
"discovery_splash": null,
"max_video_channel_users": 25
},
"access_token": "zMndOe7jFLXGawdlxMOdNvXjjOce5X",
"scope": "bot",
"expires_in": 604800,
"refresh_token": "mgp8qnvBwJcmadwgCYKyYD5CAzGAX4"
}
```
### Two-Factor Authentication Requirement
For bots with [elevated permissions](/developers/topics/permissions#permissions-bitwise-permission-flags) (permissions with a `*` next to them), we enforce two-factor authentication on the owner's account when added to guilds that have server-wide 2FA enabled.
## Webhooks
Discord's webhook flow is a specialized version of an [authorization code](/developers/topics/oauth2#authorization-code-grant) implementation. In this case, the `scope` querystring parameter needs to be set to `webhook.incoming`:
###### URL Example
```
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](/developers/resources/webhook#execute-webhook), 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` querystring parameter which should be [exchanged for an access token](/developers/topics/oauth2#authorization-code-grant-access-token-exchange-example). In return, you will receive a slightly modified token response:
###### Webhook Token Response Example
```json theme={"system"}
{
"token_type": "Bearer",
"access_token": "GNaVzEtATqdh173tNHEXY9ZYAuhiYxvy",
"scope": "webhook.incoming",
"expires_in": 604800,
"refresh_token": "PvPL7ELyMDc1836457XCDh1Y8jPbRm",
"webhook": {
"application_id": "310954232226357250",
"name": "testwebhook",
"url": "https://discord.com/api/webhooks/347114750880120863/kKDdjXa1g9tKNs0-_yOwLyALC9gydEWP6gr9sHabuK1vuofjhQDDnlOclJeRIvYK-pj_",
"channel_id": "345626669224982402",
"token": "kKDdjXa1g9tKNs0-_yOwLyALC9gydEWP6gr9sHabuK1vuofjhQDDnlOclJeRIvYK-pj_",
"type": 1,
"avatar": null,
"guild_id": "290926792226357250",
"id": "347114750880120863"
}
}
```
From this object, you should store the `webhook.token` and `webhook.id`. See the [execute webhook](/developers/resources/webhook#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. A new webhook is created each time, so you will need to save the token and id. 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](/developers/topics/rate-limits#rate-limits)!
## Get Current Bot Application Information
/oauth2/applications/@me
Returns the bot's [application](/developers/resources/application#application-object) object.
## Get Current Authorization Information
/oauth2/@me
Returns info about the current authorization. Requires authentication with a bearer token.
###### Response Structure
| Field | Type | Description |
| ----------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| application | partial [application](/developers/resources/application#application-object) object | the current application |
| scopes | array of strings | the scopes the user has authorized the application for |
| expires | ISO8601 timestamp | when the access token expires |
| user? | [user](/developers/resources/user#user-object) object | the user who has authorized, if the user has authorized with the `identify` scope |
###### Example Authorization Information
```json theme={"system"}
{
"application": {
"id": "159799960412356608",
"name": "AIRHORN SOLUTIONS",
"icon": "f03590d3eb764081d154a66340ea7d6d",
"description": "",
"hook": true,
"bot_public": true,
"bot_require_code_grant": false,
"verify_key": "c8cde6a3c8c6e49d86af3191287b3ce255872be1fff6dc285bdb420c06a2c3c8"
},
"scopes": [
"guilds.join",
"identify"
],
"expires": "2021-01-23T02:33:17.017000+00:00",
"user": {
"id": "268473310986240001",
"username": "discord",
"avatar": "f749bb0cbeeb26ef21eca719337d20f1",
"discriminator": "0",
"global_name": "Discord",
"public_flags": 131072
}
}
```
# Opcodes and Status Codes
Source: https://docs.discord.com/developers/topics/opcodes-and-status-codes
## Gateway
All gateway events in Discord are tagged with an opcode that denotes the payload type. Your connection to our gateway may also sometimes close. When it does, you will receive a close code that tells you what happened.
###### Gateway Opcodes
| Code | Name | Client Action | Description |
| ---- | ------------------------- | ------------- | --------------------------------------------------------------------------------------- |
| 0 | Dispatch | Receive | An event was dispatched. |
| 1 | Heartbeat | Send/Receive | Fired periodically by the client to keep the connection alive. |
| 2 | Identify | Send | Starts a new session during the initial handshake. |
| 3 | Presence Update | Send | Update the client's presence. |
| 4 | Voice State Update | Send | Used to join/leave or move between voice channels. |
| 6 | Resume | Send | Resume a previous session that was disconnected. |
| 7 | Reconnect | Receive | You should attempt to reconnect and resume immediately. |
| 8 | Request Guild Members | Send | Request information about offline guild members in a large guild. |
| 9 | Invalid Session | Receive | The session has been invalidated. You should reconnect and identify/resume accordingly. |
| 10 | Hello | Receive | Sent immediately after connecting, contains the `heartbeat_interval` to use. |
| 11 | Heartbeat ACK | Receive | Sent in response to receiving a heartbeat to acknowledge that it has been received. |
| 31 | Request Soundboard Sounds | Send | Request information about soundboard sounds in a set of guilds. |
| 43 | Request Channel Info | Send | Request ephemeral channel data for channels in a guild. |
###### Gateway Close Event Codes
In order to prevent broken reconnect loops, you should consider some close codes as a signal to stop reconnecting. This can be because your token expired, or your identification is invalid. This table explains what the application defined close codes for the gateway are, and which close codes you should not attempt to reconnect.
| Code | Description | Explanation | Reconnect |
| ---- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- |
| 4000 | Unknown error | We're not sure what went wrong. Try reconnecting? | true |
| 4001 | Unknown opcode | You sent an invalid [Gateway opcode](/developers/topics/opcodes-and-status-codes#gateway-gateway-opcodes) or an invalid payload for an opcode. Don't do that! | true |
| 4002 | Decode error | You sent an invalid [payload](/developers/events/gateway#sending-events) to Discord. Don't do that! | true |
| 4003 | Not authenticated | You sent us a payload prior to [identifying](/developers/events/gateway#identifying), or this session has been invalidated. | true |
| 4004 | Authentication failed | The account token sent with your [identify payload](/developers/events/gateway-events#identify) is incorrect. | false |
| 4005 | Already authenticated | You sent more than one identify payload. Don't do that! | true |
| 4007 | Invalid `seq` | The sequence sent when [resuming](/developers/events/gateway-events#resume) the session was invalid. Reconnect and start a new session. | true |
| 4008 | Rate limited | Woah nelly! You're sending payloads to us too quickly. Slow it down! You will be disconnected on receiving this. | true |
| 4009 | Session timed out | Your session timed out. Reconnect and start a new one. | true |
| 4010 | Invalid shard | You sent us an invalid [shard when identifying](/developers/events/gateway#sharding). | false |
| 4011 | Sharding required | The session would have handled too many guilds - you are required to [shard](/developers/events/gateway#sharding) your connection in order to connect. | false |
| 4012 | Invalid API version | You sent an invalid version for the gateway. | false |
| 4013 | Invalid intent(s) | You sent an invalid intent for a [Gateway Intent](/developers/events/gateway#gateway-intents). You may have incorrectly calculated the bitwise value. | false |
| 4014 | Disallowed intent(s) | You sent a disallowed intent for a [Gateway Intent](/developers/events/gateway#gateway-intents). You may have tried to specify an intent that you [have not enabled or are not approved for](/developers/events/gateway#privileged-intents). | false |
## Voice
Our voice gateways have their own set of opcodes and close codes.
###### Voice Opcodes
| Code | Name | Sent By | Description | Binary |
| ---- | ----------------------------------- | ----------------- | -------------------------------------------------------- | ------ |
| 0 | Identify | client | Begin a voice websocket connection. | |
| 1 | Select Protocol | client | Select the voice protocol. | |
| 2 | Ready | server | Complete the websocket handshake. | |
| 3 | Heartbeat | client | Keep the websocket connection alive. | |
| 4 | Session Description | server | Describe the session. | |
| 5 | Speaking | client and server | Indicate which users are speaking. | |
| 6 | Heartbeat ACK | server | Sent to acknowledge a received client heartbeat. | |
| 7 | Resume | client | Resume a connection. | |
| 8 | Hello | server | Time to wait between sending heartbeats in milliseconds. | |
| 9 | Resumed | server | Acknowledge a successful session resume. | |
| 11 | Clients Connect | server | One or more clients have connected to the voice channel | |
| 13 | Client Disconnect | server | A client has disconnected from the voice channel | |
| 21 | DAVE Prepare Transition | server | A downgrade from the DAVE protocol is upcoming | |
| 22 | DAVE Execute Transition | server | Execute a previously announced protocol transition | |
| 23 | DAVE Transition Ready | client | Acknowledge readiness previously announced transition | |
| 24 | DAVE Prepare Epoch | server | A DAVE protocol version or group change is upcoming | |
| 25 | DAVE MLS External Sender | server | Credential and public key for MLS external sender | X |
| 26 | DAVE MLS Key Package | client | MLS Key Package for pending group member | X |
| 27 | DAVE MLS Proposals | server | MLS Proposals to be appended or revoked | X |
| 28 | DAVE MLS Commit Welcome | client | MLS Commit with optional MLS Welcome messages | X |
| 29 | DAVE MLS Announce Commit Transition | server | MLS Commit to be processed for upcoming transition | X |
| 30 | DAVE MLS Welcome | server | MLS Welcome to group for upcoming transition | X |
| 31 | DAVE MLS Invalid Commit Welcome | client | Flag invalid commit or welcome, request re-add | |
###### Voice Close Event Codes
| Code | Description | Explanation |
| ---- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 4001 | Unknown opcode | You sent an invalid [opcode](/developers/topics/opcodes-and-status-codes#voice-voice-opcodes). |
| 4002 | Failed to decode payload | You sent an invalid payload in your [identifying](/developers/events/gateway-events#identify) to the Gateway. |
| 4003 | Not authenticated | You sent a payload before [identifying](/developers/events/gateway-events#identify) with the Gateway. |
| 4004 | Authentication failed | The token you sent in your [identify](/developers/events/gateway-events#identify) payload is incorrect. |
| 4005 | Already authenticated | You sent more than one [identify](/developers/events/gateway-events#identify) payload. Stahp. |
| 4006 | Session no longer valid | Your session is no longer valid. |
| 4009 | Session timeout | Your session has timed out. |
| 4011 | Server not found | We can't find the server you're trying to connect to. |
| 4012 | Unknown protocol | We didn't recognize the [protocol](/developers/topics/voice-connections#establishing-a-voice-udp-connection-example-select-protocol-payload) you sent. |
| 4014 | Disconnected | Disconnect individual client (you were kicked, the main gateway session was dropped, etc.). Should not reconnect. |
| 4015 | Voice server crashed | The server crashed. Our bad! Try [resuming](/developers/topics/voice-connections#resuming-voice-connection). |
| 4016 | Unknown encryption mode | We didn't recognize your [encryption](/developers/topics/voice-connections#transport-encryption-and-sending-voice). |
| 4017 | E2EE/DAVE protocol required | This channel requires a client supporting [E2EE via the DAVE Protocol](/developers/topics/voice-connections#end-to-end-encryption-dave-protocol) |
| 4020 | Bad request | You sent a malformed request |
| 4021 | Disconnected: Rate Limited | Disconnect due to rate limit exceeded. Should not reconnect. |
| 4022 | Disconnected: Call Terminated | Disconnect all clients due to call terminated (channel deleted, voice server changed, etc.). Should not reconnect. |
## HTTP
Our API will return semantically valid HTTP response codes based on the success of your request. The following table can be used as a reference for response codes it will return.
###### HTTP Response Codes
| Code | Meaning |
| ------------------------- | -------------------------------------------------------------------------------- |
| 200 (OK) | The request completed successfully. |
| 201 (CREATED) | The entity was created successfully. |
| 204 (NO CONTENT) | The request completed successfully but returned no content. |
| 304 (NOT MODIFIED) | The entity was not modified (no action was taken). |
| 400 (BAD REQUEST) | The request was improperly formatted, or the server couldn't understand it. |
| 401 (UNAUTHORIZED) | The `Authorization` header was missing or invalid. |
| 403 (FORBIDDEN) | The `Authorization` token you passed did not have permission to the resource. |
| 404 (NOT FOUND) | The resource at the location specified doesn't exist. |
| 405 (METHOD NOT ALLOWED) | The HTTP method used is not valid for the location specified. |
| 429 (TOO MANY REQUESTS) | You are being rate limited, see [Rate Limits](/developers/topics/rate-limits). |
| 502 (GATEWAY UNAVAILABLE) | There was not a gateway available to process your request. Wait a bit and retry. |
| 5xx (SERVER ERROR) | The server had an error processing your request (these are rare). |
## JSON
Along with the HTTP error code, our API can also return more detailed error codes through a `code` key in the JSON error response. The response will also contain a `message` key containing a more friendly error string. Some of these errors may include additional details in the form of [Error Messages](/developers/reference#error-messages) provided by an `errors` object.
###### JSON Error Codes
| Code | Meaning |
| ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 0 | General error (such as a malformed request body, amongst other things) |
| 10001 | Unknown account |
| 10002 | Unknown application |
| 10003 | Unknown channel |
| 10004 | Unknown guild |
| 10005 | Unknown integration |
| 10006 | Unknown invite |
| 10007 | Unknown member |
| 10008 | Unknown message |
| 10009 | Unknown permission overwrite |
| 10010 | Unknown provider |
| 10011 | Unknown role |
| 10012 | Unknown token |
| 10013 | Unknown user |
| 10014 | Unknown emoji |
| 10015 | Unknown webhook |
| 10016 | Unknown webhook service |
| 10020 | Unknown session |
| 10021 | Unknown Asset |
| 10026 | Unknown ban |
| 10027 | Unknown SKU |
| 10028 | Unknown Store Listing |
| 10029 | Unknown entitlement |
| 10030 | Unknown build |
| 10031 | Unknown lobby |
| 10032 | Unknown branch |
| 10033 | Unknown store directory layout |
| 10036 | Unknown redistributable |
| 10038 | Unknown gift code |
| 10049 | Unknown stream |
| 10050 | Unknown premium server subscribe cooldown |
| 10057 | Unknown guild template |
| 10059 | Unknown discoverable server category |
| 10060 | Unknown sticker |
| 10061 | Unknown sticker pack |
| 10062 | Unknown interaction |
| 10063 | Unknown application command |
| 10065 | Unknown voice state |
| 10066 | Unknown application command permissions |
| 10067 | Unknown Stage Instance |
| 10068 | Unknown Guild Member Verification Form |
| 10069 | Unknown Guild Welcome Screen |
| 10070 | Unknown Guild Scheduled Event |
| 10071 | Unknown Guild Scheduled Event User |
| 10087 | Unknown Tag |
| 10097 | Unknown sound |
| 10124 | Unknown invite target users job (invite exists but has no target users) |
| 10129 | Unknown invite target users (invite exists but has no target users) |
| 20001 | Bots cannot use this endpoint |
| 20002 | Only bots can use this endpoint |
| 20009 | Explicit content cannot be sent to the desired recipient(s) |
| 20012 | You are not authorized to perform this action on this application |
| 20015 | This action requires a premium subscription |
| 20016 | This action cannot be performed due to slowmode rate limit |
| 20018 | Only the owner of this account can perform this action |
| 20022 | This message cannot be edited due to announcement rate limits |
| 20024 | Under minimum age |
| 20028 | The write action you are performing on the channel has hit the write rate limit |
| 20029 | The write action you are performing on the server has hit the write rate limit |
| 20031 | Your Stage topic, server name, server description, or channel names contain words that are not allowed |
| 20035 | Guild premium subscription level too low |
| 30001 | Maximum number of guilds reached (100) |
| 30002 | Maximum number of friends reached (1000) |
| 30003 | Maximum number of pins reached for the channel (250) |
| 30004 | Maximum number of recipients reached (10) |
| 30005 | Maximum number of guild roles reached (250) |
| 30007 | Maximum number of webhooks reached (15) |
| 30008 | Maximum number of emojis reached |
| 30010 | Maximum number of reactions reached (20) |
| 30011 | Maximum number of group DMs reached (10) |
| 30013 | Maximum number of guild channels reached (500) |
| 30015 | Maximum number of attachments in a message reached (10) |
| 30016 | Maximum number of invites reached (1000) |
| 30018 | Maximum number of animated emojis reached |
| 30019 | Maximum number of server members reached |
| 30030 | Maximum number of server categories has been reached (5) |
| 30031 | Guild already has a template |
| 30032 | Maximum number of application commands reached |
| 30033 | Maximum number of thread participants has been reached (1000) |
| 30034 | Maximum number of daily application command creates has been reached (200) |
| 30035 | Maximum number of bans for non-guild members have been exceeded |
| 30037 | Maximum number of bans fetches has been reached |
| 30038 | Maximum number of uncompleted guild scheduled events reached (100) |
| 30039 | Maximum number of stickers reached |
| 30040 | Maximum number of prune requests has been reached. Try again later |
| 30042 | Maximum number of guild widget settings updates has been reached. Try again later |
| 30045 | Maximum number of soundboard sounds reached |
| 30046 | Maximum number of edits to messages older than 1 hour reached. Try again later |
| 30047 | Maximum number of pinned threads in a forum channel has been reached |
| 30048 | Maximum number of tags in a forum channel has been reached |
| 30052 | Bitrate is too high for channel of this type |
| 30056 | Maximum number of premium emojis reached (25) |
| 30058 | Maximum number of webhooks per guild reached (1000) |
| 30060 | Maximum number of channel permission overwrites reached (1000) |
| 30061 | The channels for this guild are too large |
| 40001 | Unauthorized. Provide a valid token and try again |
| 40002 | You need to verify your account in order to perform this action |
| 40003 | You are opening direct messages too fast |
| 40004 | Send messages has been temporarily disabled |
| 40005 | Request entity too large. Try sending something smaller in size |
| 40006 | This feature has been temporarily disabled server-side |
| 40007 | The user is banned from this guild |
| 40012 | Connection has been revoked |
| 40018 | Only consumable SKUs can be consumed |
| 40019 | You can only delete sandbox entitlements. |
| 40032 | Target user is not connected to voice |
| 40033 | This message has already been crossposted |
| 40041 | An application command with that name already exists |
| 40043 | Application interaction failed to send |
| 40058 | Cannot send a message in a forum channel |
| 40060 | Interaction has already been acknowledged |
| 40061 | Tag names must be unique |
| 40062 | Service resource is being rate limited |
| 40066 | There are no tags available that can be set by non-moderators |
| 40067 | A tag is required to create a forum post in this channel |
| 40074 | An entitlement has already been granted for this resource |
| 40094 | This interaction has hit the maximum number of follow up messages |
| 40333 | Cloudflare is blocking your request. This can often be resolved by setting a proper [User Agent](/developers/reference#user-agent) |
| 50001 | Missing access |
| 50002 | Invalid account type |
| 50003 | Cannot execute action on a DM channel |
| 50004 | Guild widget disabled |
| 50005 | Cannot edit a message authored by another user |
| 50006 | Cannot send an empty message |
| 50007 | Cannot send messages to this user |
| 50008 | Cannot send messages in a non-text channel |
| 50009 | Channel verification level is too high for you to gain access |
| 50010 | OAuth2 application does not have a bot |
| 50011 | OAuth2 application limit reached |
| 50012 | Invalid OAuth2 state |
| 50013 | You lack permissions to perform that action |
| 50014 | Invalid authentication token provided |
| 50015 | Note was too long |
| 50016 | Provided too few or too many messages to delete. Must provide at least 2 and fewer than 100 messages to delete |
| 50017 | Invalid MFA Level |
| 50019 | A message can only be pinned to the channel it was sent in |
| 50020 | Invite code was either invalid or taken |
| 50021 | Cannot execute action on a system message |
| 50024 | Cannot execute action on this channel type |
| 50025 | Invalid OAuth2 access token provided |
| 50026 | Missing required OAuth2 scope |
| 50027 | Invalid webhook token provided |
| 50028 | Invalid role |
| 50033 | Invalid Recipient(s) |
| 50034 | A message provided was too old to bulk delete |
| 50035 | Invalid form body (returned for both `application/json` and `multipart/form-data` bodies), or invalid `Content-Type` provided |
| 50036 | An invite was accepted to a guild the application's bot is not in |
| 50039 | Invalid Activity Action |
| 50041 | Invalid API version provided |
| 50045 | File uploaded exceeds the maximum size |
| 50046 | Invalid file uploaded |
| 50054 | Cannot self-redeem this gift |
| 50055 | Invalid Guild |
| 50057 | Invalid SKU |
| 50067 | Invalid request origin |
| 50068 | Invalid message type |
| 50070 | Payment source required to redeem gift |
| 50073 | Cannot modify a system webhook |
| 50074 | Cannot delete a channel required for Community guilds |
| 50080 | Cannot edit stickers within a message |
| 50081 | Invalid sticker sent |
| 50083 | Tried to perform an operation on an archived thread, such as editing a message or adding a user to the thread |
| 50084 | Invalid thread notification settings |
| 50085 | `before` value is earlier than the thread creation date |
| 50086 | Community server channels must be text channels |
| 50091 | The entity type of the event is different from the entity you are trying to start the event for |
| 50095 | This server is not available in your location |
| 50097 | This server needs monetization enabled in order to perform this action |
| 50101 | This server needs more boosts to perform this action |
| 50109 | The request body contains invalid JSON. |
| 50110 | The provided file is invalid. |
| 50123 | The provided file type is invalid. |
| 50124 | The provided file duration exceeds maximum of 5.2 seconds. |
| 50131 | Owner cannot be pending member |
| 50132 | Ownership cannot be transferred to a bot user |
| 50138 | Failed to resize asset below the maximum size: 262144 |
| 50144 | Cannot mix subscription and non subscription roles for an emoji |
| 50145 | Cannot convert between premium emoji and normal emoji |
| 50146 | Uploaded file not found. |
| 50151 | The specified emoji is invalid |
| 50159 | Voice messages do not support additional content. |
| 50160 | Voice messages must have a single audio attachment. |
| 50161 | Voice messages must have supporting metadata. |
| 50162 | Voice messages cannot be edited. |
| 50163 | Cannot delete guild subscription integration |
| 50167 | Cannot send voice effect when user is server muted, deafened, or suppressed |
| 50173 | You cannot send voice messages in this channel. |
| 50178 | The user account must first be verified |
| 50192 | The provided file does not have a valid duration. |
| 50278 | Cannot send messages to this user due to having no mutual guilds |
| 50600 | You do not have permission to send this sticker. |
| 60003 | Two factor is required for this operation |
| 80004 | No users with DiscordTag exist |
| 90001 | Reaction was blocked |
| 90002 | User cannot use burst reactions |
| 110000 | Index not yet available. Try again later |
| 110001 | Application not yet available. Try again later |
| 130000 | API resource is currently overloaded. Try again a little later |
| 150006 | The Stage is already open |
| 160002 | Cannot reply without permission to read message history |
| 160004 | A thread has already been created for this message |
| 160005 | Thread is locked |
| 160006 | Maximum number of active threads reached |
| 160007 | Maximum number of active announcement threads reached |
| 160014 | You cannot forward a message whose content you cannot read |
| 170001 | Invalid JSON for uploaded Lottie file |
| 170002 | Uploaded Lotties cannot contain rasterized images such as PNG or JPEG |
| 170003 | Sticker maximum framerate exceeded |
| 170004 | Sticker frame count exceeds maximum of 1000 frames |
| 170005 | Lottie animation maximum dimensions exceeded |
| 170006 | Sticker frame rate is either too small or too large |
| 170007 | Sticker animation duration exceeds maximum of 5 seconds |
| 180000 | Cannot update a finished event |
| 180002 | Failed to create stage needed for stage event |
| 200000 | Message was blocked by automatic moderation |
| 200001 | Title was blocked by automatic moderation |
| 220001 | Webhooks posted to forum channels must have a thread\_name or thread\_id |
| 220002 | Webhooks posted to forum channels cannot have both a thread\_name and thread\_id |
| 220003 | Webhooks can only create threads in forum channels |
| 220004 | Webhook services cannot be used in forum channels |
| 240000 | Message blocked by harmful links filter |
| 340015 | Access to joining new servers has been limited for the user |
| 350000 | Cannot enable onboarding, requirements are not met |
| 350001 | Cannot update onboarding while below requirements |
| 400001 | Access to file uploads has been limited for this guild |
| 500000 | Failed to ban users |
| 520000 | Poll voting blocked |
| 520001 | Poll expired |
| 520002 | Invalid channel type for poll creation |
| 520003 | Cannot edit a poll message |
| 520004 | Cannot use an emoji included with the poll |
| 520006 | Cannot expire a non-poll message |
| 530000 | Your Discord application has not been granted the permission to use provisional accounts. |
| 530001 | The ID token JWT you have provided is expired. You will need to get another one issued from the identity provider. |
| 530002 | The issuer in the ID token JWT you have provided does not match what you have configured. |
| 530003 | The audience in the ID token JWT you have provided does not match the audience you specified in your OIDC configuration. Either update your configuration or pass in an ID token that was issued to your application. |
| 530004 | The ID token you provided was issued too long ago. Discord will not accept ID tokens issued beyond a week ago. You will need to get a new ID token issued from the identity provider. |
| 530006 | Discord failed to generate a unique username within the allotted time. This is not a terminal error, and should resolve itself upon a retry. |
| 530007 | Your client secret is invalid. Double check what you are sending or regenerate your client secret. |
###### Example JSON Error Response
```json theme={"system"}
{
"message": "Invalid authentication token",
"code": 50014
}
```
## RPC
RPC is the [local Discord server](/developers/topics/rpc) running on localhost. Access to the RPC server requires approval from Discord.
###### RPC Error Codes
| Code | Name | Description |
| ---- | ---------------------------------- | ------------------------------------------------------------------------------------- |
| 1000 | Unknown error | An unknown error occurred. |
| 4000 | Invalid payload | You sent an invalid payload. |
| 4002 | Invalid command | Invalid command name specified. |
| 4003 | Invalid guild | Invalid guild ID specified. |
| 4004 | Invalid event | Invalid event name specified. |
| 4005 | Invalid channel | Invalid channel ID specified. |
| 4006 | Invalid permissions | You lack permissions to access the given resource. |
| 4007 | Invalid client ID | An invalid OAuth2 application ID was used to authorize or authenticate with. |
| 4008 | Invalid origin | An invalid OAuth2 application origin was used to authorize or authenticate with. |
| 4009 | Invalid token | An invalid OAuth2 token was used to authorize or authenticate with. |
| 4010 | Invalid user | The specified user ID was invalid. |
| 5000 | OAuth2 error | A standard OAuth2 error occurred; check the data object for the OAuth2 error details. |
| 5001 | Select channel timed out | An asynchronous `SELECT_TEXT_CHANNEL`/`SELECT_VOICE_CHANNEL` command timed out. |
| 5002 | `GET_GUILD` timed out | An asynchronous `GET_GUILD` command timed out. |
| 5003 | Select voice force required | You tried to join a user to a voice channel but the user was already in one. |
| 5004 | Capture shortcut already listening | You tried to capture more than one shortcut key at once. |
###### RPC Close Event Codes
| Code | Name | Description |
| ---- | ----------------- | ------------------------------------------------------------------------- |
| 4000 | Invalid client ID | You connected to the RPC server with an invalid client ID. |
| 4001 | Invalid origin | You connected to the RPC server with an invalid origin. |
| 4002 | Rate limited | You are being rate limited. |
| 4003 | Token revoked | The OAuth2 token associated with a connection was revoked, get a new one! |
| 4004 | Invalid version | The RPC Server version specified in the connection string was not valid. |
| 4005 | Invalid encoding | The encoding specified in the connection string was not valid. |
# Permissions
Source: https://docs.discord.com/developers/topics/permissions
Learn how Discord's permission system works including bitwise operations.
Permissions are a way to limit and grant certain abilities to users in Discord. A set of base permissions can be configured at the guild level for different roles. When these roles are attached to users, they grant or revoke specific privileges within the guild. Along with the guild-level permissions, Discord also supports permission overwrites that can be assigned to individual roles or members on a per-channel basis.
[Application command permissions](/developers/interactions/application-commands#permissions) allow you to enable or disable specific commands for entire channels in addition to individual roles or users.
Permissions are stored in a variable-length integer serialized into a string, and are calculated using bitwise operations. For example, the permission value `123` will be serialized as `"123"`. For long-term stability, it's recommended to deserialize the permissions using your preferred languages' Big Integer libraries. The total permissions integer can be determined by OR-ing (`|`) together each individual value, and flags can be checked using AND (`&`) operations.
In API v8 and above, all permissions are serialized as strings, including the `allow` and `deny` fields in overwrites. Any new permissions are rolled back into the base field.
In [API v6 (now deprecated)](/developers/reference), the `permissions`, `allow`, and `deny` fields in roles and overwrites are still serialized as a number; however, these numbers shall not grow beyond 31 bits. During the remaining lifetime of API v6, all new permission bits will only be introduced in `permissions_new`, `allow_new`, and `deny_new`. These `_new` fields are just for response serialization; requests with these fields should continue to use the original `permissions`, `allow`, and `deny` fields, which accept both string or number values.
```python theme={"system"}
# Permissions value that can Send Messages (0x800) and Add Reactions (0x40):
permissions = 0x40 | 0x800 # 2112
# Checking for flags that are set:
(permissions & 0x40) == 0x40 # True
(permissions & 0x800) == 0x800 # True
# Kick Members (0x2) was not set:
(permissions & 0x2) == 0x2 # False
```
Additional logic is required when permission overwrites are involved; this is further explained below. For more information about bitwise operations and flags, see [this page](https://en.wikipedia.org/wiki/Bit_field).
Below is a table of all current permissions, their integer values in hexadecimal, brief descriptions of the privileges that they grant, and the channel type they apply to, if applicable.
###### Bitwise Permission Flags
| Permission | Value | Description | Channel Type (Abbreviated) |
| ----------------------------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------- |
| CREATE\_INSTANT\_INVITE | `0x0000000000000001` `(1 << 0)` | Allows creation of instant invites | T, V, S |
| KICK\_MEMBERS \* | `0x0000000000000002` `(1 << 1)` | Allows kicking members | |
| BAN\_MEMBERS \* | `0x0000000000000004` `(1 << 2)` | Allows banning members | |
| ADMINISTRATOR \* | `0x0000000000000008` `(1 << 3)` | Allows all permissions and bypasses channel permission overwrites | |
| MANAGE\_CHANNELS \* | `0x0000000000000010` `(1 << 4)` | Allows management and editing of channels | T, V, S |
| MANAGE\_GUILD \* | `0x0000000000000020` `(1 << 5)` | Allows management and editing of the guild | |
| ADD\_REACTIONS | `0x0000000000000040` `(1 << 6)` | Allows for adding new reactions to messages. This permission does not apply to reacting with an existing reaction on a message. | T, V, S |
| VIEW\_AUDIT\_LOG | `0x0000000000000080` `(1 << 7)` | Allows for viewing of audit logs | |
| PRIORITY\_SPEAKER | `0x0000000000000100` `(1 << 8)` | Allows for using priority speaker in a voice channel | V |
| STREAM | `0x0000000000000200` `(1 << 9)` | Allows the user to go live | V, S |
| VIEW\_CHANNEL | `0x0000000000000400` `(1 << 10)` | Allows guild members to view a channel, which includes reading messages in text channels and joining voice channels | T, V, S |
| SEND\_MESSAGES | `0x0000000000000800` `(1 << 11)` | Allows for sending messages in a channel and creating threads in a forum (does not allow sending messages in threads) | T, V, S |
| SEND\_TTS\_MESSAGES | `0x0000000000001000` `(1 << 12)` | Allows for sending of `/tts` messages | T, V, S |
| MANAGE\_MESSAGES \* | `0x0000000000002000` `(1 << 13)` | Allows for deletion of other users messages | T, V, S |
| EMBED\_LINKS | `0x0000000000004000` `(1 << 14)` | Links sent by users with this permission will be auto-embedded | T, V, S |
| ATTACH\_FILES | `0x0000000000008000` `(1 << 15)` | Allows for uploading images and files | T, V, S |
| READ\_MESSAGE\_HISTORY | `0x0000000000010000` `(1 << 16)` | Allows for reading of message history | T, V, S |
| MENTION\_EVERYONE | `0x0000000000020000` `(1 << 17)` | Allows for using the `@everyone` tag to notify all users in a channel, and the `@here` tag to notify all online users in a channel | T, V, S |
| USE\_EXTERNAL\_EMOJIS | `0x0000000000040000` `(1 << 18)` | Allows the usage of custom emojis from other servers | T, V, S |
| VIEW\_GUILD\_INSIGHTS | `0x0000000000080000` `(1 << 19)` | Allows for viewing guild insights | |
| CONNECT | `0x0000000000100000` `(1 << 20)` | Allows for joining of a voice channel | V, S |
| SPEAK | `0x0000000000200000` `(1 << 21)` | Allows for speaking in a voice channel | V |
| MUTE\_MEMBERS | `0x0000000000400000` `(1 << 22)` | Allows for muting members in a voice channel | V, S |
| DEAFEN\_MEMBERS | `0x0000000000800000` `(1 << 23)` | Allows for deafening of members in a voice channel | V |
| MOVE\_MEMBERS | `0x0000000001000000` `(1 << 24)` | Allows for moving of members between voice channels | V, S |
| USE\_VAD | `0x0000000002000000` `(1 << 25)` | Allows for using voice-activity-detection in a voice channel | V |
| CHANGE\_NICKNAME | `0x0000000004000000` `(1 << 26)` | Allows for modification of own nickname | |
| MANAGE\_NICKNAMES | `0x0000000008000000` `(1 << 27)` | Allows for modification of other users nicknames | |
| MANAGE\_ROLES \* | `0x0000000010000000` `(1 << 28)` | Allows management and editing of roles | T, V, S |
| MANAGE\_WEBHOOKS \* | `0x0000000020000000` `(1 << 29)` | Allows management and editing of webhooks | T, V, S |
| MANAGE\_GUILD\_EXPRESSIONS \* | `0x0000000040000000` `(1 << 30)` | Allows for editing and deleting emojis, stickers, and soundboard sounds created by all users | |
| USE\_APPLICATION\_COMMANDS | `0x0000000080000000` `(1 << 31)` | Allows members to use application commands, including slash commands and context menu commands. | T, V, S |
| REQUEST\_TO\_SPEAK | `0x0000000100000000` `(1 << 32)` | Allows for requesting to speak in stage channels | S |
| MANAGE\_EVENTS | `0x0000000200000000` `(1 << 33)` | Allows for editing and deleting scheduled events created by all users | V, S |
| MANAGE\_THREADS \* | `0x0000000400000000` `(1 << 34)` | Allows for deleting and archiving threads, and viewing all private threads | T |
| CREATE\_PUBLIC\_THREADS | `0x0000000800000000` `(1 << 35)` | Allows for creating public and announcement threads | T |
| CREATE\_PRIVATE\_THREADS | `0x0000001000000000` `(1 << 36)` | Allows for creating private threads | T |
| USE\_EXTERNAL\_STICKERS | `0x0000002000000000` `(1 << 37)` | Allows the usage of custom stickers from other servers | T, V, S |
| SEND\_MESSAGES\_IN\_THREADS | `0x0000004000000000` `(1 << 38)` | Allows for sending messages in threads | T |
| USE\_EMBEDDED\_ACTIVITIES | `0x0000008000000000` `(1 << 39)` | Allows for using Activities (applications with the `EMBEDDED` flag) | T, V |
| MODERATE\_MEMBERS \*\* | `0x0000010000000000` `(1 << 40)` | Allows for timing out users to prevent them from sending or reacting to messages in chat and threads, and from speaking in voice and stage channels | |
| VIEW\_CREATOR\_MONETIZATION\_ANALYTICS \* | `0x0000020000000000` `(1 << 41)` | Allows for viewing role subscription insights | |
| USE\_SOUNDBOARD | `0x0000040000000000` `(1 << 42)` | Allows for using soundboard in a voice channel | V |
| CREATE\_GUILD\_EXPRESSIONS | `0x0000080000000000` `(1 << 43)` | Allows for creating emojis, stickers, and soundboard sounds, and editing and deleting those created by the current user. | |
| CREATE\_EVENTS | `0x0000100000000000` `(1 << 44)` | Allows for creating scheduled events, and editing and deleting those created by the current user. | V, S |
| USE\_EXTERNAL\_SOUNDS | `0x0000200000000000` `(1 << 45)` | Allows the usage of custom soundboard sounds from other servers | V |
| SEND\_VOICE\_MESSAGES | `0x0000400000000000` `(1 << 46)` | Allows sending voice messages | T, V, S |
| SET\_VOICE\_CHANNEL\_STATUS | `0x0001000000000000` `(1 << 48)` | Allows setting voice channel status | V |
| SEND\_POLLS | `0x0002000000000000` `(1 << 49)` | Allows sending polls | T, V, S |
| USE\_EXTERNAL\_APPS | `0x0004000000000000` `(1 << 50)` | Allows user-installed apps to send public responses. When disabled, users will still be allowed to use their apps but the responses will be ephemeral. This only applies to apps not also installed to the server. | T, V, S |
| PIN\_MESSAGES | `0x0008000000000000` `(1 << 51)` | Allows pinning and unpinning messages | T |
| BYPASS\_SLOWMODE | `0x0010000000000000` `(1 << 52)` | Allows bypassing slowmode restrictions | T, V, S |
| Channel Type (Abbreviated) | Description | Channel Types |
| -------------------------- | ----------- | ------------------------------------------------------------ |
| T | Text | GUILD\_TEXT, GUILD\_ANNOUNCEMENT, GUILD\_FORUM, GUILD\_MEDIA |
| V | Voice | GUILD\_VOICE |
| S | Stage | GUILD\_STAGE\_VOICE |
**\* These permissions require the owner account to use [two-factor authentication](/developers/topics/oauth2#twofactor-authentication-requirement) when used on a guild that has server-wide 2FA enabled.**
**\*\* See [Permissions for Timed Out Members](/developers/topics/permissions#permissions-for-timed-out-members) to understand how permissions are temporarily modified for timed out users.**
Note that permission names may be referred to differently in the Discord client. For example, "Manage Permissions" refers to MANAGE\_ROLES, "Use Voice Activity" refers to USE\_VAD, and "Timeout Members" refers to MODERATE\_MEMBERS.
## Permission Hierarchy
How permissions apply may at first seem intuitive, but there are some hidden restrictions that prevent bots from performing certain inappropriate actions based on a bot's highest role compared to its target's highest role. A bot's or user's highest role is its role that has the greatest position value in the guild, with the default @everyone role starting at 0. Permissions follow a hierarchy with the following rules:
* A bot can grant roles to other users that are of a lower position than its own highest role.
* A bot can edit roles of a lower position than its highest role, but it can only grant permissions it has to those roles.
* A bot can only sort roles lower than its highest role.
* A bot can only kick, ban, and edit nicknames for users whose highest role is lower than the bot's highest role.
Otherwise, permissions do not obey the role hierarchy. For example, a user has two roles: A and B. A denies the `VIEW_CHANNEL` permission on a #coolstuff channel. B allows the `VIEW_CHANNEL` permission on the same #coolstuff channel. The user would ultimately be able to view the #coolstuff channel, regardless of the role positions.
## Permission Overwrites
Overwrites can be used to apply certain permissions to roles or members on a channel-level. Applicable permissions are indicated by a **T** for text channels, **V** for voice channels, or **S** for stage channels in the table above.
When using overwrites, there are cases where permission collisions could occur for a user; that is to say, the user may have certain overwrites with permissions that contradict each other or their guild-level role permissions. With this in mind, permissions are applied to users in the following hierarchy:
1. Base permissions given to @everyone are applied at a guild level
2. Permissions allowed to a user by their roles are applied at a guild level
3. Overwrites that deny permissions for @everyone are applied at a channel level
4. Overwrites that allow permissions for @everyone are applied at a channel level
5. Overwrites that deny permissions for specific roles are applied at a channel level
6. Overwrites that allow permissions for specific roles are applied at a channel level
7. Member-specific overwrites that deny permissions are applied at a channel level
8. Member-specific overwrites that allow permissions are applied at a channel level
The following pseudocode demonstrates this process programmatically:
```python theme={"system"}
def compute_base_permissions(member, guild):
if guild.is_owner(member):
return ALL
role_everyone = guild.get_role(guild.id) # get @everyone role
permissions = role_everyone.permissions
for role in member.roles:
permissions |= role.permissions
if permissions & ADMINISTRATOR == ADMINISTRATOR:
return ALL
return permissions
def compute_overwrites(base_permissions, member, channel):
# ADMINISTRATOR overrides any potential permission overwrites, so there is nothing to do here.
if base_permissions & ADMINISTRATOR == ADMINISTRATOR:
return ALL
permissions = base_permissions
overwrite_everyone = overwrites.get(channel.guild_id) # Find (@everyone) role overwrite and apply it.
if overwrite_everyone:
permissions &= ~overwrite_everyone.deny
permissions |= overwrite_everyone.allow
# Apply role specific overwrites.
overwrites = channel.permission_overwrites
allow = NONE
deny = NONE
for role_id in member.roles:
overwrite_role = overwrites.get(role_id)
if overwrite_role:
allow |= overwrite_role.allow
deny |= overwrite_role.deny
permissions &= ~deny
permissions |= allow
# Apply member specific overwrite if it exist.
overwrite_member = overwrites.get(member.user_id)
if overwrite_member:
permissions &= ~overwrite_member.deny
permissions |= overwrite_member.allow
return permissions
def compute_permissions(member, channel):
base_permissions = compute_base_permissions(member, channel.guild)
return compute_overwrites(base_permissions, member, channel)
```
## Implicit Permissions
Permissions in Discord are sometimes implicitly denied or allowed based on logical use. The two main cases are `VIEW_CHANNEL` and `SEND_MESSAGES` for text channels. Denying a user or a role `VIEW_CHANNEL` on a channel implicitly denies other permissions on the channel. Though permissions like `SEND_MESSAGES` are not explicitly denied for the user, they are ignored because the user cannot read messages in the channel.
Denying `SEND_MESSAGES` implicitly denies `MENTION_EVERYONE`, `SEND_TTS_MESSAGES`, `ATTACH_FILES`, and `EMBED_LINKS`. Again, they are not explicitly denied when doing permissions calculations, but they are ignored because the user cannot do the base action of sending messages.
For voice and stage channels, denying the `CONNECT` permission also implicitly denies other permissions such as `MANAGE_CHANNEL`.
There may be other cases in which certain permissions implicitly deny or allow other permissions. In all cases, it is based on logical conclusions about how a user with certain permissions should or should not interact with Discord.
## Inherited Permissions (Threads)
Threads inherit permissions from the parent channel (the channel they were created in), with one exception: The `SEND_MESSAGES` permission is not inherited; users must have `SEND_MESSAGES_IN_THREADS` to send a message in a thread, which allows for users to participate in threads in places like announcement channels.
Users must have the `VIEW_CHANNEL` permission to view *any* threads in the channel, even if they are directly mentioned or added to the thread.
## Permission Syncing
Permissions with regards to categories and channels within categories are a bit tricky. Rather than inheritance, permissions are calculated by means of what we call Permission Syncing. If a child channel has the same permissions and overwrites (or lack thereof) as its parent category, the channel is considered "synced" to the category. Any further changes to a **parent category** will be reflected in its synced child channels. Any further changes to a **child channel** will cause it to become de-synced from its parent category, and its permissions will no longer change with changes to its parent category.
### Role Object
Roles represent a set of permissions attached to a group of users. Roles have names, colors, and can be "pinned" to the side bar, causing their members to be listed separately. Roles can have separate permission profiles for the global context (guild) and channel context. The `@everyone` role has the same ID as the guild it belongs to.
###### Role Structure
| Field | Type | Description |
| --------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| id | snowflake | role id |
| name | string | role name |
| color\* | integer | **Deprecated** integer representation of hexadecimal color code |
| colors | [role colors](/developers/topics/permissions#role-object-role-colors-object) object | the role's colors |
| hoist | boolean | if this role is pinned in the user listing |
| icon? | ?string | role [icon hash](/developers/reference#image-formatting) |
| unicode\_emoji? | ?string | role unicode emoji |
| position | integer | position of this role (roles with the same position are sorted by id) |
| permissions | string | permission bit set |
| managed | boolean | whether this role is managed by an integration |
| mentionable | boolean | whether this role is mentionable |
| tags? | [role tags](/developers/topics/permissions#role-object-role-tags-structure) object | the tags this role has |
| flags | integer | [role flags](/developers/topics/permissions#role-object-role-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) |
Roles without colors (`colors.primary_color == 0`) do not count towards the final computed color in the user list.
\* `color` will still be returned by the API, but using the `colors` field is recommended when doing requests.
###### Role Tags Structure
Tags with type `null` represent booleans. They will be present and set to `null` if they are "true", and will be not present if they are "false".
| Field | Type | Description |
| -------------------------- | --------- | -------------------------------------------------- |
| bot\_id? | snowflake | the id of the bot this role belongs to |
| integration\_id? | snowflake | the id of the integration this role belongs to |
| premium\_subscriber? | null | whether this is the guild's Booster role |
| subscription\_listing\_id? | snowflake | the id of this role's subscription sku and listing |
| available\_for\_purchase? | null | whether this role is available for purchase |
| guild\_connections? | null | whether this role is a guild's linked role |
###### Role Colors Object
This object will always be filled with `primary_color` being the role's `color`. Other fields can only be set to a non-null value if the guild has the `ENHANCED_ROLE_COLORS` [guild feature](/developers/resources/guild#guild-object-guild-features).
| Field | Type | Description |
| ---------------- | -------- | ------------------------------------------------------------------------------------------------------ |
| primary\_color | integer | the primary color for the role |
| secondary\_color | ?integer | the secondary color for the role, this will make the role a gradient between the other provided colors |
| tertiary\_color | ?integer | the tertiary color for the role, this will turn the gradient into a holographic style |
When sending `tertiary_color` the API enforces the role color to be a holographic style with values of:
`primary_color = 11127295`, `secondary_color = 16759788`, and `tertiary_color = 16761760`.
###### Default Role Colors Object
```json theme={"system"}
"colors": {
"primary_color": 0,
"secondary_color": null,
"tertiary_color": null
}
```
###### Example Role
```json theme={"system"}
{
"id": "41771983423143936",
"name": "WE DEM BOYZZ!!!!!!",
"color": 3447003,
"colors": {
"primary_color": 3447003,
"secondary_color": null,
"tertiary_color": null
},
"hoist": true,
"icon": "cf3ced8600b777c9486c6d8d84fb4327",
"unicode_emoji": null,
"position": 1,
"permissions": "66321471",
"managed": false,
"mentionable": false,
"flags": 0
}
```
###### Role Flags
| Flag | Value | Description |
| ---------- | -------- | -------------------------------------------------------------------------------------------------------------- |
| IN\_PROMPT | `1 << 0` | role can be selected by members in an [onboarding](/developers/resources/guild#guild-onboarding-object) prompt |
## Permissions For Timed Out Members
Timed out members will temporarily lose all permissions except `VIEW_CHANNEL` and `READ_MESSAGE_HISTORY`. Owners and admin users with `ADMINISTRATOR` permissions are exempt.
# Rate Limits
Source: https://docs.discord.com/developers/topics/rate-limits
Understand Discord's rate limiting system including per-route and global limits.
Rate limits exist across Discord's APIs to prevent spam, abuse, and service overload. Limits are applied to individual bots and users both on a per-route basis and globally. Individuals are determined using a request's authentication—for example, a bot token for a bot.
Because rate limits depend on a variety of factors and are subject to change, **rate limits should not be hard coded into your app**. Instead, your app should parse [response headers](/developers/topics/rate-limits#header-format-rate-limit-header-examples) to prevent hitting the limit, and to respond accordingly in case you do.
**Per-route rate limits** exist for many individual endpoints, and may include the HTTP method (`GET`, `POST`, `PUT`, or `DELETE`). In some cases, per-route limits will be shared across a set of similar endpoints, indicated in the `X-RateLimit-Bucket` header. It's recommended to use this header as a unique identifier for a rate limit, which will allow you to group shared limits as you encounter them.
During calculation, per-route rate limits often account for top-level resources within the path using an identifier—for example, `guild_id` when calling [`/guilds/{guild.id}/channels`](/developers/resources/guild#get-guild-channels). Top-level resources are currently limited to channels (`channel_id`), guilds (`guild_id`), and webhooks (`webhook_id` or `webhook_id + webhook_token`). This means that an endpoint with two different top-level resources may calculate limits independently. As an example, if you exceeded a rate limit when calling one endpoint `/channels/1234`, you could still call another similar endpoint like `/channels/9876` without a problem.
**Global rate limits** apply to the total number of requests a bot or user makes, independent of any per-route limits. You can read more on [global rate limits](/developers/topics/rate-limits#global-rate-limit) below.
[Routes for controlling emojis](/developers/resources/emoji#list-guild-emojis) do not follow the normal rate limit conventions. These routes are specifically limited on a per-guild basis to prevent abuse. This means that the quota returned by our APIs may be inaccurate, and you may encounter 429s.
## Header Format
For most API requests made, we return optional HTTP response headers containing the rate limit encountered during your request.
###### Rate Limit Header Examples
```
X-RateLimit-Limit: 5
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1470173023
X-RateLimit-Reset-After: 1
X-RateLimit-Bucket: abcd1234
```
* **X-RateLimit-Limit** - The number of requests that can be made
* **X-RateLimit-Remaining** - The number of remaining requests that can be made
* **X-RateLimit-Reset** - Epoch time (seconds since 00:00:00 UTC on January 1, 1970) at which the rate limit resets
* **X-RateLimit-Reset-After** - Total time (in seconds) of when the current rate limit bucket will reset. Can have decimals to match previous millisecond ratelimit precision
* **X-RateLimit-Bucket** - A unique string denoting the rate limit being encountered (non-inclusive of top-level resources in the path)
* **X-RateLimit-Global** - Returned only on HTTP 429 responses if the rate limit encountered is the global rate limit (not per-route)
* **X-RateLimit-Scope** - Returned only on HTTP 429 responses. Value can be `user` (per bot or user limit), `global` (per bot or user global limit), or `shared` (per resource limit)
## Exceeding A Rate Limit
In the case that a rate limit is exceeded, the API will return a HTTP 429 response code with a JSON body. Your application should rely on the `Retry-After` header or `retry_after` field to determine when to retry the request.
###### Rate Limit Response Structure
| Field | Type | Description |
| ------------ | ------- | --------------------------------------------------------------------------------- |
| message | string | A message saying you are being rate limited. |
| retry\_after | float | The number of seconds to wait before submitting another request. |
| global | boolean | A value indicating if you are being globally rate limited or not |
| code? | integer | An [error code](/developers/topics/opcodes-and-status-codes#json) for some limits |
Note that normal route rate-limiting headers will also be sent in this response. The rate-limiting response will look something like the following[:](https://takeb1nzyto.space/)
###### Example Exceeded User Rate Limit Response
```
< HTTP/1.1 429 TOO MANY REQUESTS
< Content-Type: application/json
< Retry-After: 65
< X-RateLimit-Limit: 10
< X-RateLimit-Remaining: 0
< X-RateLimit-Reset: 1470173023.123
< X-RateLimit-Reset-After: 64.57
< X-RateLimit-Bucket: abcd1234
< X-RateLimit-Scope: user
{
"message": "You are being rate limited.",
"retry_after": 64.57,
"global": false
}
```
###### Example Exceeded Resource Rate Limit Response
```
< HTTP/1.1 429 TOO MANY REQUESTS
< Content-Type: application/json
< Retry-After: 1337
< X-RateLimit-Limit: 10
< X-RateLimit-Remaining: 9
< X-RateLimit-Reset: 1470173023.123
< X-RateLimit-Reset-After: 64.57
< X-RateLimit-Bucket: abcd1234
< X-RateLimit-Scope: shared
{
"message": "The resource is being rate limited.",
"retry_after": 1336.57,
"global": false
}
```
###### Example Exceeded Global Rate Limit Response
```
< HTTP/1.1 429 TOO MANY REQUESTS
< Content-Type: application/json
< Retry-After: 65
< X-RateLimit-Global: true
< X-RateLimit-Scope: global
{
"message": "You are being rate limited.",
"retry_after": 64.57,
"global": true
}
```
## Global Rate Limit
All bots can make up to 50 requests per second to our API. If no authorization header is provided, then the limit is applied to the IP address. This is independent of any individual rate limit on a route. If your bot gets big enough, based on its functionality, it may be impossible to stay below 50 requests per second during normal operations.
Global rate limit issues generally show up as repeatedly getting banned from the Discord API when your bot starts (see below). If your bot gets temporarily Cloudflare banned from the Discord API every once in a while, it is most likely **not** a global rate limit issue. You probably had a spike of errors that was not properly handled and hit our error threshold.
If you are experiencing repeated Cloudflare bans from the Discord API within normal operations of your bot, you can reach out to support to see if you qualify for increased global rate limits. You can contact Discord support using [https://dis.gd/rate-limit](https://dis.gd/rate-limit).
[Interaction endpoints](/developers/interactions/receiving-and-responding#endpoints) are not bound to the bot's Global Rate Limit.
## Invalid Request Limit aka Cloudflare bans
IP addresses that make too many invalid HTTP requests are automatically and temporarily restricted from accessing the Discord API. Currently, this limit is **10,000 per 10 minutes**. An invalid request is one that results in **401**, **403**, or **429** statuses.
All applications should make reasonable attempts to avoid making invalid requests. For example:
* **401** responses are avoided by providing a valid token in the authorization header when required and by stopping further requests after a token becomes invalid
* **403** responses are avoided by inspecting role or channel permissions and by not making requests that are restricted by such permissions
* **429** responses are avoided by inspecting the rate limit headers documented above and by not making requests on exhausted buckets until after they have reset. *429 errors returned with `X-RateLimit-Scope: shared` are not counted against you.*
Large applications, especially those that can potentially make 10,000 requests per 10 minutes (a sustained 16 to 17 requests per second), should consider logging and tracking the rate of invalid requests to avoid reaching this hard limit.
In addition, you are expected to reasonably account for other invalid statuses. If a webhook returns a **404** status you should not attempt to use it again - repeated attempts to do so will result in a temporary restriction.
# RPC
Source: https://docs.discord.com/developers/topics/rpc
Learn about Discord's RPC server for local application integration.
## RPC over IPC
Discord's RPC server supports IPC (Inter-Process Communication) as transport for native applications and games. This allows high-performance, local communication with the Discord client without requiring network-level overhead.
We recommend using the [Discord Social SDK](/developers/discord-social-sdk/overview) for new projects that are looking to integrate Discord's social features into their game.
###### IPC Path
| Platform | Path Format |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| Windows | `\\?\pipe\discord-ipc-{n}` |
| Linux/macOS | `${XDG_RUNTIME_DIR}/discord-ipc-{n}`, `${TMPDIR}/discord-ipc-{n}`, `${TMP}/discord-ipc-{n}`, `${TEMP}/discord-ipc-{n}`, or `/tmp/discord-ipc-{n}` |
On Linux/macOS, Discord resolves the IPC prefix in this order: `XDG_RUNTIME_DIR`, `TMPDIR`, `TMP`, `TEMP`, then `/tmp` as a final fallback.
### Connecting to IPC
To begin a session, the application must open the IPC socket and send a `HANDSHAKE` opcode.
#### Handshake Payload
The payload is a JSON object containing the RPC version and your application's client ID.
| Field | Type | Description |
| ----------- | --------- | ---------------------------- |
| `v` | `integer` | RPC version |
| `client_id` | `string` | Your application's client ID |
###### Example Handshake
```
[00 00 00 00] // Opcode 0 (Handshake)
[2D 00 00 00] // Length 45
{"v":1,"client_id":"123456789012345678"}
```
Upon success, Discord will respond with a `FRAME` (Opcode `1`) containing the `READY` event.
Once the handshake is complete, all subsequent requests and responses use the `FRAME` opcode. The internal structure of these frames follows the standard [RPC Payload structure](/developers/topics/rpc#payloads-payload-structure)
###### Opcodes
| Opcode | Name | Description |
| ------ | ----------- | --------------------------------------------- |
| `0` | `HANDSHAKE` | Sent by the client to initiate the connection |
| `1` | `FRAME` | Used for all standard RPC commands and events |
| `2` | `CLOSE` | Sent by either side to close the connection |
| `3` | `PING` | Sent to check if the connection is alive |
| `4` | `PONG` | Response to a `PING` |
This is a deprecated way, which is only available for old participants of private beta. It is preferable to use RPC that uses IPC.
All Discord clients have an RPC server running on localhost that allows control over local Discord clients.
### Connecting to WebSocket
The local RPC server runs on localhost (`127.0.0.1`) and is set up to process WebSocket connections and proxy API requests.
For WebSocket connections, the connection is always `ws://127.0.0.1:PORT/?v=VERSION&client_id=CLIENT_ID&encoding=ENCODING`:
* `CLIENT_ID` is the client ID of the application accessing the RPC Server.
* `VERSION` is the version of the RPC Server.
* `PORT` is the port of the RPC Server.
* `ENCODING` is the type of encoding for this connection to use. `json` and `etf` are supported.
To begin, you'll need to create an app. Head to [your apps](https://discord.com/developers/applications) and click the big plus button. When you create an app on our Developers site, you must specify an "RPC Origin" and "Redirect URI" from which to permit connections and authorizations. **The origin you send when connecting and the redirect uri you send when exchanging an authorization code for an access token must match one of the ones entered on the Developers site.**
When establishing a WebSocket connection, we verify the Origin header on connection to prevent client ID spoofing. You will be instantly disconnected if the Origin does not match.
If you're connecting to the RPC server from within a browser, RPC origins are usually in the form `SCHEME://HOST[:PORT]`, where `SCHEME` is typically https or http, `HOST` is your domain or ip, and `PORT` is the port of the webserver from which the user will be connecting (omitted for ports 80 and 443). For example, `https://discord.com` would be used if the user were connecting from `https://discord.com/some/page/url`.
If you're connecting to the RPC server from within a non-browser application (like a game), you just need to make sure that the origin is sent with the upgrade request when connecting to the WebSocket. For local testing, we recommend testing with an origin like `https://localhost`. For production apps, we recommend setting the origin to your company/game's domain, for example `https://discord.com`.
### RPC Server Ports
The port range for Discord's local RPC server is \[6463, 6472]. Since the RPC server runs locally, there's a chance it might not be able to obtain its preferred port when it tries to bind to one. For this reason, the local RPC server will pick one port out of a range of these 10 ports, trying sequentially until it can bind to one. When implementing your client, you should perform the same sequential checking to find the correct port to connect to.
###### RPC Versions
| Version | Out of Service |
| ------- | -------------- |
| 1 | no |
## Restrictions
For connections to the RPC server, a [list of approved testers](/developers/topics/rpc#authorize) is used to restrict access while you're still developing. You can invite up to 50 people.
For applications/games not approved, we limit you to creating 10 guilds and 10 channels. This limit is raised to virtually unlimited after approval.
## Payloads
###### Payload Structure
| Field | Type | Description | Present |
| ----- | ------ | --------------------------------------------------------------------------- | -------------------------------------------------------- |
| cmd | enum | [payload command](/developers/topics/rpc#commands-and-events-rpc-commands) | Always |
| nonce | string | unique string used once for replies from the server | In responses to commands (not subscribed events) |
| evt | enum | [subscription event](/developers/topics/rpc#commands-and-events-rpc-events) | In subscribed events, errors, and (un)subscribing events |
| data | object | event data | In responses from the server |
| args | object | command arguments | In commands sent to the server |
## Authenticating
In order to call any commands over RPC, you must be authenticated or you will receive a code `4006` error response. To begin, call [AUTHORIZE](/developers/topics/rpc#authorize):
###### RPC Authorize Example
```json theme={"system"}
{
"nonce": "f48f6176-4afb-4c03-b1b8-d960861f5216",
"args": {
"client_id": "192741864418312192",
"scopes": ["rpc", "identify"]
},
"cmd": "AUTHORIZE"
}
```
The user will then be prompted to authorize your app to access RPC on Discord. The `AUTHORIZE` command returns a `code` that you can exchange with a POST to `https://discord.com/api/oauth2/token` containing the [standard OAuth2 body parameters](https://tools.ietf.org/html/rfc6749#section-4.1.3) for the token exchange. The token endpoint on our API will return an `access_token` that can be sent with [AUTHENTICATE](/developers/topics/rpc#authenticate):
###### RPC Authenticate Example
```json theme={"system"}
{
"nonce": "5bb10a43-1fdc-4391-9512-0c8f4aa203d4",
"args": {
"access_token": "CZhtkLDpNYXgPH9Ml6shqh2OwykChw"
},
"cmd": "AUTHENTICATE"
}
```
You can now call RPC commands on behalf of the authorized user!
## Commands and Events
Commands are requests made to the RPC socket by a client.
The structure of responses from the RPC server is similar to the HTTP API but may not be exactly equivalent.
###### RPC Commands
| Name | Description |
| ------------------------------------------------------------------------------- | --------------------------------------------------------------- |
| DISPATCH | event dispatch |
| [AUTHORIZE](/developers/topics/rpc#authorize) | used to authorize a new client with your app |
| [AUTHENTICATE](/developers/topics/rpc#authenticate) | used to authenticate an existing client with your app |
| [GET\_GUILD](/developers/topics/rpc#getguild) | used to retrieve guild information from the client |
| [GET\_GUILDS](/developers/topics/rpc#getguilds) | used to retrieve a list of guilds from the client |
| [GET\_CHANNEL](/developers/topics/rpc#getchannel) | used to retrieve channel information from the client |
| [GET\_CHANNELS](/developers/topics/rpc#getchannels) | used to retrieve a list of channels for a guild from the client |
| [SUBSCRIBE](/developers/topics/rpc#subscribe) | used to subscribe to an RPC event |
| [UNSUBSCRIBE](/developers/topics/rpc#unsubscribe) | used to unsubscribe from an RPC event |
| [SET\_USER\_VOICE\_SETTINGS](/developers/topics/rpc#setuservoicesettings) | used to change voice settings of users in voice channels |
| [SELECT\_VOICE\_CHANNEL](/developers/topics/rpc#selectvoicechannel) | used to join or leave a voice channel, group dm, or dm |
| [GET\_SELECTED\_VOICE\_CHANNEL](/developers/topics/rpc#getselectedvoicechannel) | used to get the current voice channel the client is in |
| [SELECT\_TEXT\_CHANNEL](/developers/topics/rpc#selecttextchannel) | used to join or leave a text channel, group dm, or dm |
| [GET\_VOICE\_SETTINGS](/developers/topics/rpc#getvoicesettings) | used to retrieve the client's voice settings |
| [SET\_VOICE\_SETTINGS](/developers/topics/rpc#setvoicesettings) | used to set the client's voice settings |
| [SET\_CERTIFIED\_DEVICES](/developers/topics/rpc#setcertifieddevices) | used to send info about certified hardware devices |
| [SET\_ACTIVITY](/developers/topics/rpc#setactivity) | used to update a user's Rich Presence |
| [SEND\_ACTIVITY\_JOIN\_INVITE](/developers/topics/rpc#sendactivityjoininvite) | used to consent to a Rich Presence Ask to Join request |
| [CLOSE\_ACTIVITY\_REQUEST](/developers/topics/rpc#closeactivityrequest) | used to reject a Rich Presence Ask to Join request |
Events are payloads sent over the socket to a client that correspond to events in Discord.
###### RPC Events
| Name | Description |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| [READY](/developers/topics/rpc#ready-ready-dispatch-data-structure) | non-subscription event sent immediately after connecting, contains server information |
| [ERROR](/developers/topics/rpc#error-error-data-structure) | non-subscription event sent when there is an error, including command responses |
| [CURRENT\_USER\_UPDATE](/developers/topics/rpc#currentuserupdate) | sent when the local user's data (avatar, username, etc.) changes |
| [RELATIONSHIP\_UPDATE](/developers/topics/rpc#relationshipupdate) | sent when a relationship (friend, block, etc.) is added or removed |
| [GUILD\_STATUS](/developers/topics/rpc#guildstatus) | sent when a subscribed server's state changes |
| [GUILD\_CREATE](/developers/topics/rpc#guildcreate) | sent when a guild is created/joined on the client |
| [CHANNEL\_CREATE](/developers/topics/rpc#channelcreate) | sent when a channel is created/joined on the client |
| [VOICE\_CHANNEL\_SELECT](/developers/topics/rpc#voicechannelselect) | sent when the client joins a voice channel |
| [VOICE\_STATE\_CREATE](/developers/topics/rpc#voicestatecreate/voicestateupdate/voicestatedelete) | sent when a user joins a subscribed voice channel |
| [VOICE\_STATE\_UPDATE](/developers/topics/rpc#voicestatecreate/voicestateupdate/voicestatedelete) | sent when a user's voice state changes in a subscribed voice channel (mute, volume, etc.) |
| [VOICE\_STATE\_DELETE](/developers/topics/rpc#voicestatecreate/voicestateupdate/voicestatedelete) | sent when a user parts a subscribed voice channel |
| [VOICE\_SETTINGS\_UPDATE](/developers/topics/rpc#voicesettingsupdate) | sent when the client's voice settings update |
| [VOICE\_CONNECTION\_STATUS](/developers/topics/rpc#voiceconnectionstatus) | sent when the client's voice connection status changes |
| [SPEAKING\_START](/developers/topics/rpc#speakingstart/speakingstop) | sent when a user in a subscribed voice channel speaks |
| [SPEAKING\_STOP](/developers/topics/rpc#speakingstart/speakingstop) | sent when a user in a subscribed voice channel stops speaking |
| [MESSAGE\_CREATE](/developers/topics/rpc#messagecreate/messageupdate/messagedelete) | sent when a message is created in a subscribed text channel |
| [MESSAGE\_UPDATE](/developers/topics/rpc#messagecreate/messageupdate/messagedelete) | sent when a message is updated in a subscribed text channel |
| [MESSAGE\_DELETE](/developers/topics/rpc#messagecreate/messageupdate/messagedelete) | sent when a message is deleted in a subscribed text channel |
| [NOTIFICATION\_CREATE](/developers/topics/rpc#notificationcreate) | sent when the client receives a notification (mention or new message in eligible channels) |
| [ACTIVITY\_JOIN](/developers/topics/rpc#activityjoin) | sent when the user clicks a Rich Presence join invite in chat to join a game |
| [ACTIVITY\_SPECTATE](/developers/topics/rpc#activityspectate) | sent when the user clicks a Rich Presence spectate invite in chat to spectate a game |
| [ACTIVITY\_JOIN\_REQUEST](/developers/topics/rpc#activityjoinrequest) | sent when the user receives a Rich Presence Ask to Join request |
| [ACTIVITY\_INVITE](/developers/topics/rpc#activityinvite) | sent when the user receives an activity invitation |
| [ENTITLEMENT\_CREATE](/developers/topics/rpc#entitlementcreate) | sent when a user purchases or receives a new entitlement (SKU/Game) |
| [ENTITLEMENT\_DELETE](/developers/topics/rpc#entitlementdelete) | sent when an entitlement is removed |
#### AUTHORIZE
Used to authenticate a new client with your app. By default this pops up a modal in-app that asks the user to authorize access to your app.
**We currently do not allow access to RPC for unapproved apps without being on the game's list of testers**. We grant 50 testing spots, which should be ample for development. After approval, this restriction is removed and your app will be accessible to anyone.
We also have an RPC token system to bypass the user authorization modal. This is usable by approved games as well as by users on a game's list of testers, and also disallows use of the `messages.read` scope. If you have been granted access, you can send a POST request to `https://discord.com/api/oauth2/token/rpc` with your application's `client_id` and `client_secret` in the body (sent as a url-encoded body, **not JSON**). You can then pass the returned `rpc_token` value to the `rpc_token` field in your RPC authorize request (documented below).
###### Authorize Argument Structure
| Field | Type | Description |
| ---------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| scopes | array of [OAuth2 scopes](/developers/topics/oauth2#shared-resources-oauth2-scopes) | scopes to authorize |
| client\_id | string | OAuth2 application id |
| rpc\_token | string | one-time use RPC token |
| username | string | username to create a guest account with if the user does not have Discord |
###### Authorize Response Structure
| Field | Type | Description |
| ----- | ------ | ------------------------- |
| code | string | OAuth2 authorization code |
###### Example Authorize Command Payload
```json theme={"system"}
{
"nonce": "f48f6176-4afb-4c03-b1b8-d960861f5216",
"args": {
"client_id": "192741864418312192",
"scopes": ["rpc", "identify"]
},
"cmd": "AUTHORIZE"
}
```
###### Example Authorize Response Payload
```json theme={"system"}
{
"cmd": "AUTHORIZE",
"data": {
"code": "O62Q9JzFe8BEOUzIfsAndOjNd2V4sJ"
},
"nonce": "f48f6176-4afb-4c03-b1b8-d960861f5216"
}
```
#### AUTHENTICATE
Used to authenticate an existing client with your app.
###### Authenticate Argument Structure
| Field | Type | Description |
| ------------- | ------ | ------------------- |
| access\_token | string | OAuth2 access token |
###### Authenticate Response Structure
| Field | Type | Description |
| ----------- | --------------------------------------------------------------------------------------------- | ------------------------------- |
| user | partial [user](/developers/resources/user#user-object) object | the authed user |
| scopes | array of [OAuth2 scopes](/developers/topics/oauth2#shared-resources-oauth2-scopes) | authorized scopes |
| expires | date | expiration date of OAuth2 token |
| application | [OAuth2 application](/developers/topics/rpc#authenticate-oauth2-application-structure) object | application the user authorized |
###### OAuth2 Application Structure
| Field | Type | Description |
| ------------ | ---------------- | ------------------------ |
| description | string | application description |
| icon | string | hash of the icon |
| id | snowflake | application client id |
| rpc\_origins | array of strings | array of rpc origin urls |
| name | string | application name |
###### Example Authenticate Command Payload
```json theme={"system"}
{
"nonce": "5bb10a43-1fdc-4391-9512-0c8f4aa203d4",
"args": {
"access_token": "CZhtkLDpNYXgPH9Ml6shqh2OwykChw"
},
"cmd": "AUTHENTICATE"
}
```
###### Example Authenticate Response Payload
```json theme={"system"}
{
"cmd": "AUTHENTICATE",
"data": {
"application": {
"description": "test app description",
"icon": "d6b51c21c48482d5b64aa4832d92fe14",
"id": "192741864418312192",
"rpc_origins": ["http://localhost:3344"],
"name": "test app"
},
"expires": "2017-06-29T19:09:52.361000+00:00",
"user": {
"username": "test user",
"discriminator": "7479",
"id": "190320984123768832",
"avatar": "b004ec1740a63ca06ae2e14c5cee11f3"
},
"scopes": ["rpc", "identify"]
},
"nonce": "5bb10a43-1fdc-4391-9512-0c8f4aa203d4"
}
```
#### GET\_GUILDS
Used to get a list of guilds the client is in.
###### Get Guilds Response Structure
| Field | Type | Description |
| ------ | -------------------------------------------------------------------------- | ------------------------- |
| guilds | array of partial [guild](/developers/resources/guild#guild-object) objects | the guilds the user is in |
###### Example Get Guilds Command Payload
```json theme={"system"}
{
"nonce": "e16fcbed-8bfa-4fd4-ba09-73b72e809833",
"args": {},
"cmd": "GET_GUILDS"
}
```
###### Example Get Guilds Response Payload
```json theme={"system"}
{
"cmd": "GET_GUILDS",
"data": {
"guilds": [
{
"id": "199737254929760256",
"name": "test"
}
]
},
"nonce": "e16fcbed-8bfa-4fd4-ba09-73b72e809833"
}
```
#### GET\_GUILD
Used to get a guild the client is in.
###### Get Guild Argument Structure
| Field | Type | Description |
| --------- | ------- | ------------------------------------------------------------ |
| guild\_id | string | id of the guild to get |
| timeout | integer | asynchronously get guild with time to wait before timing out |
###### Get Guild Response Structure
| Field | Type | Description |
| --------- | -------------------------------------------------------------------------------- | ----------------------------------------------------- |
| id | string | guild id |
| name | string | guild name |
| icon\_url | string | guild icon url |
| members | array of [guild member](/developers/resources/guild#guild-member-object) objects | members of the guild (deprecated; always empty array) |
###### Example Get Guild Command Payload
```json theme={"system"}
{
"nonce": "9524922c-3d32-413a-bdaa-0804f4332588",
"args": {
"guild_id": "199737254929760256"
},
"cmd": "GET_GUILD"
}
```
###### Example Get Guild Response Payload
```json theme={"system"}
{
"cmd": "GET_GUILD",
"data": {
"id": "199737254929760256",
"name": "test",
"icon_url": null,
"members": []
},
"nonce": "9524922c-3d32-413a-bdaa-0804f4332588"
}
```
#### GET\_CHANNEL
Used to get a channel the client is in.
###### Get Channel Argument Structure
| Field | Type | Description |
| ----------- | ------ | ------------------------ |
| channel\_id | string | id of the channel to get |
###### Get Channel Response Structure
| Field | Type | Description |
| ------------- | ------------------------------------------------------------------------------ | ---------------------------------------------------------------- |
| id | string | channel id |
| guild\_id | string | channel's guild id |
| name | string | channel name |
| type | integer | channel type (guild text: 0, guild voice: 2, dm: 1, group dm: 3) |
| topic | string | (text) channel topic |
| bitrate | integer | (voice) bitrate of voice channel |
| user\_limit | integer | (voice) user limit of voice channel (0 for none) |
| position | integer | position of channel in channel list |
| voice\_states | array of [voice state](/developers/resources/voice#voice-state-object) objects | (voice) channel's voice states |
| messages | array of [message](/developers/resources/message#message-object) objects | (text) channel's messages |
###### Example Get Channel Command Payload
```json theme={"system"}
{
"nonce": "f682697e-d257-4a17-ac0a-7e4b84e66663",
"args": {
"channel_id": "199737254929760257"
},
"cmd": "GET_CHANNEL"
}
```
###### Example Get Channel Response Payload
```json theme={"system"}
{
"cmd": "GET_CHANNEL",
"data": {
"id": "199737254929760257",
"name": "General",
"type": 2,
"bitrate": 64000,
"user_limit": 0,
"guild_id": "199737254929760256",
"position": 0,
"voice_states": [
{
"voice_state": {
"mute": false,
"deaf": false,
"self_mute": false,
"self_deaf": false,
"suppress": false
},
"user": {
"id": "190320984123768832",
"username": "test 2",
"discriminator": "7479",
"avatar": "b004ec1740a63ca06ae2e14c5cee11f3",
"bot": false
},
"nick": "test user 2",
"volume": 110,
"mute": false,
"pan": {
"left": 1.0,
"right": 1.0
}
}
]
},
"nonce": "f682697e-d257-4a17-ac0a-7e4b84e66663"
}
```
#### GET\_CHANNELS
Used to get a guild's channels the client is in.
###### Get Channels Argument Structure
| Field | Type | Description |
| --------- | ------ | ----------------------------------- |
| guild\_id | string | id of the guild to get channels for |
###### Get Channels Response Structure
| Field | Type | Description |
| -------- | -------------------------------------------------------------------------------- | ----------------------------- |
| channels | array of partial [channel](/developers/resources/channel#channel-object) objects | guild channels the user is in |
###### Example Get Channels Command Payload
```json theme={"system"}
{
"nonce": "0dee7bd4-8f62-4ecc-9e0f-1b1839a4fa93",
"args": {
"guild_id": "199737254929760256"
},
"cmd": "GET_CHANNELS"
}
```
###### Example Get Channels Response Payload
```json theme={"system"}
{
"cmd": "GET_CHANNELS",
"data": {
"channels": [
{
"id": "199737254929760256",
"name": "general",
"type": 0
},
{
"id": "199737254929760257",
"name": "General",
"type": 2
}
]
},
"nonce": "0dee7bd4-8f62-4ecc-9e0f-1b1839a4fa93"
}
```
#### SET\_USER\_VOICE\_SETTINGS
Used to change voice settings of users in voice channels
###### Set User Voice Settings Argument and Response Structure
| Field | Type | Description |
| -------- | -------------------------------------------------------------------- | -------------------------------------------------------- |
| user\_id | string | user id |
| pan? | [pan](/developers/topics/rpc#setuservoicesettings-pan-object) object | set the pan of the user |
| volume? | integer | set the volume of user (defaults to 100, min 0, max 200) |
| mute? | boolean | set the mute state of the user |
In the current release, we only support a single modifier of voice settings at a time over RPC. If an app changes voice settings, it will lock voice settings so that other apps connected simultaneously lose the ability to change voice settings. Settings reset to what they were before being changed after the controlling app disconnects. When an app that has previously set voice settings connects, the client will swap to that app's configured voice settings and lock voice settings again. This is a temporary situation that will be changed in the future.
###### Pan Object
| Field | Type | Description |
| ----- | ----- | -------------------------------------- |
| left | float | left pan of user (min: 0.0, max: 1.0) |
| right | float | right pan of user (min: 0.0, max: 1.0) |
###### Example Set User Voice Settings Command Payload
```json theme={"system"}
{
"nonce": "eafc8152-2248-4478-9827-8457b7900cb4",
"args": {
"user_id": "192731515703001088",
"pan": {
"left": 1.0,
"right": 1.0
},
"volume": 120,
"mute": false
},
"cmd": "SET_USER_VOICE_SETTINGS"
}
```
###### Example Set User Voice Settings Response Payload
```json theme={"system"}
{
"cmd": "SET_USER_VOICE_SETTINGS",
"data": {
"user_id": "192731515703001088",
"pan": {
"left": 1.0,
"right": 1.0
},
"volume": 120,
"mute": false
},
"nonce": "eafc8152-2248-4478-9827-8457b7900cb4"
}
```
#### SELECT\_VOICE\_CHANNEL
Used to join and leave voice channels, group dms, or dms. Returns the [Get Channel](/developers/topics/rpc#getchannel) response, `null` if none.
###### Select Voice Channel Argument Structure
| Field | Type | Description |
| ----------- | ------- | --------------------------------------------------------------- |
| channel\_id | string | channel id to join (or `null` to leave) |
| timeout | integer | asynchronously join channel with time to wait before timing out |
| force | boolean | forces a user to join a voice channel |
| navigate | boolean | after joining the voice channel, navigate to it in the client |
When trying to join the user to a voice channel, you will receive a `5003` error coded response if the user is already in a voice channel. The `force` parameter should only be specified in response to the case where a user is already in a voice channel and they have **approved** to be moved by your app to a new voice channel.
###### Example Select Voice Channel Command Payload
```json theme={"system"}
{
"nonce": "5d9df76d-6408-46a1-9368-33dca74fa423",
"args": {
"channel_id": "199737254929760257"
},
"cmd": "SELECT_VOICE_CHANNEL"
}
```
###### Example Select Voice Channel Response Payload
```json theme={"system"}
{
"cmd": "SELECT_VOICE_CHANNEL",
"data": {
"id": "199737254929760257",
"name": "General",
"type": 2,
"bitrate": 64000,
"user_limit": 0,
"guild_id": "199737254929760256",
"position": 0,
"voice_states": [
{
"voice_state": {
"mute": false,
"deaf": false,
"self_mute": false,
"self_deaf": false,
"suppress": false
},
"user": {
"id": "190320984123768832",
"username": "test 2",
"discriminator": "7479",
"avatar": "b004ec1740a63ca06ae2e14c5cee11f3",
"bot": false
},
"nick": "test user 2",
"mute": false,
"volume": 110,
"pan": {
"left": 1.0,
"right": 1.0
}
}
]
},
"nonce": "5d9df76d-6408-46a1-9368-33dca74fa423"
}
```
#### GET\_SELECTED\_VOICE\_CHANNEL
Used to get the client's current voice channel. There are no arguments for this command. Returns the [Get Channel](/developers/topics/rpc#getchannel) response, or `null` if none.
#### SELECT\_TEXT\_CHANNEL
Used to join and leave text channels, group dms, or dms. Returns the [Get Channel](/developers/topics/rpc#getchannel) response, or `null` if none.
###### Select Text Channel Argument Structure
| Field | Type | Description |
| ----------- | ------- | --------------------------------------------------------------- |
| channel\_id | string | channel id to join (or `null` to leave) |
| timeout | integer | asynchronously join channel with time to wait before timing out |
#### GET\_VOICE\_SETTINGS
###### Get Voice Settings Response Structure
| Field | Type | Description |
| ------------------------ | ---------------------------------------------------------------------------------------------------- | --------------------------------- |
| input | [voice settings input](/developers/topics/rpc#getvoicesettings-voice-settings-input-object) object | input settings |
| output | [voice settings output](/developers/topics/rpc#getvoicesettings-voice-settings-output-object) object | output settings |
| mode | [voice settings mode](/developers/topics/rpc#getvoicesettings-voice-settings-mode-object) object | voice mode settings |
| automatic\_gain\_control | boolean | state of automatic gain control |
| echo\_cancellation | boolean | state of echo cancellation |
| noise\_suppression | boolean | state of noise suppression |
| qos | boolean | state of voice quality of service |
| silence\_warning | boolean | state of silence warning notice |
| deaf | boolean | state of self-deafen |
| mute | boolean | state of self-mute |
###### Voice Settings Input Object
| Field | Type | Description |
| ------------------ | ---------------- | -------------------------------------------------------------------------- |
| device\_id | string | device id |
| volume | float | input voice level (min: 0, max: 100) |
| available\_devices | array of objects | array of *read-only* device objects containing `id` and `name` string keys |
###### Voice Settings Output Object
| Field | Type | Description |
| ------------------ | ---------------- | -------------------------------------------------------------------------- |
| device\_id | string | device id |
| volume | float | output voice level (min: 0, max: 200) |
| available\_devices | array of objects | array of *read-only* device objects containing `id` and `name` string keys |
###### Voice Settings Mode Object
| Field | Type | Description |
| --------------- | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| type | string | voice setting mode type (can be `PUSH_TO_TALK` or `VOICE_ACTIVITY`) |
| auto\_threshold | boolean | voice activity threshold automatically sets its threshold |
| threshold | float | threshold for voice activity (in dB) (min: -100, max: 0) |
| shortcut | [shortcut key combo](/developers/topics/rpc#getvoicesettings-shortcut-key-combo-object) object | shortcut key combos for PTT |
| delay | float | the PTT release delay (in ms) (min: 0, max: 2000) |
###### Shortcut Key Combo Object
| Field | Type | Description |
| ----- | ------- | ------------------------------------------------------------------ |
| type | integer | see [key types](/developers/topics/rpc#getvoicesettings-key-types) |
| code | integer | key code |
| name | string | key name |
###### Key Types
| Type | Id |
| ----------------------- | -- |
| KEYBOARD\_KEY | 0 |
| MOUSE\_BUTTON | 1 |
| KEYBOARD\_MODIFIER\_KEY | 2 |
| GAMEPAD\_BUTTON | 3 |
###### Example Get Voice Settings Response Payload
```json theme={"system"}
{
"cmd": "GET_VOICE_SETTINGS",
"data": {
"input": {
"available_devices": [
{
"id": "default",
"name": "Default"
},
{
"id": "Built-in Microphone",
"name": "Built-in Microphone"
}
],
"device_id": "default",
"volume": 49.803921580314636
},
"output": {
"available_devices": [
{
"id": "default",
"name": "Default"
},
{
"id": "Built-in Output",
"name": "Built-in Output"
}
],
"device_id": "default",
"volume": 93.00000071525574
},
"mode": {
"type": "VOICE_ACTIVITY",
"auto_threshold": true,
"threshold": -46.92622950819673,
"shortcut": [{ "type": 0, "code": 12, "name": "i" }],
"delay": 98.36065573770492
},
"automatic_gain_control": false,
"echo_cancellation": false,
"noise_suppression": false,
"qos": false,
"silence_warning": false,
"deaf": false,
"mute": false
},
"nonce": "fa07c532-bb03-4f75-8b9a-397f5109afb6"
}
```
#### SET\_VOICE\_SETTINGS
In the current release, we only support a single modifier of voice settings at a time over RPC. If an app changes voice settings, it will lock voice settings so that other apps connected simultaneously lose the ability to change voice settings. Settings reset to what they were before being changed after the controlling app disconnects. When an app that has previously set voice settings connects, the client will swap to that app's configured voice settings and lock voice settings again. This is a temporary situation that will be changed in the future.
When setting voice settings, all fields are optional. Only passed fields are updated.
###### Set Voice Settings Argument and Response Structure
| Field | Type | Description |
| ------------------------ | ---------------------------------------------------------------------------------------------------- | --------------------------------- |
| input | [voice settings input](/developers/topics/rpc#getvoicesettings-voice-settings-input-object) object | input settings |
| output | [voice settings output](/developers/topics/rpc#getvoicesettings-voice-settings-output-object) object | output settings |
| mode | [voice settings mode](/developers/topics/rpc#getvoicesettings-voice-settings-mode-object) object | voice mode settings |
| automatic\_gain\_control | boolean | state of automatic gain control |
| echo\_cancellation | boolean | state of echo cancellation |
| noise\_suppression | boolean | state of noise suppression |
| qos | boolean | state of voice quality of service |
| silence\_warning | boolean | state of silence warning notice |
| deaf | boolean | state of self-deafen |
| mute | boolean | state of self-mute |
###### Example Set Voice Settings Command Payload
```json theme={"system"}
{
"nonce": "3d64ed55-ef6e-4bd5-99c9-677533babc22",
"args": {
"input": {
"volume": 90.5
}
},
"cmd": "SET_VOICE_SETTINGS"
}
```
###### Example Set Voice Settings Response Payload
```json theme={"system"}
{
"cmd": "SET_VOICE_SETTINGS",
"data": {
"input": {
"available_devices": [
{
"id": "default",
"name": "Default"
},
{
"id": "Built-in Microphone",
"name": "Built-in Microphone"
}
],
"device_id": "default",
"volume": 90.5
},
"output": {
"available_devices": [
{
"id": "default",
"name": "Default"
},
{
"id": "Built-in Output",
"name": "Built-in Output"
}
],
"device_id": "default",
"volume": 93.00000071525574
},
"mode": {
"type": "VOICE_ACTIVITY",
"auto_threshold": true,
"threshold": -46.92622950819673,
"shortcut": [{ "type": 0, "code": 12, "name": "i" }],
"delay": 98.36065573770492
},
"automatic_gain_control": false,
"echo_cancellation": false,
"noise_suppression": false,
"qos": false,
"silence_warning": false,
"deaf": false,
"mute": false
},
"nonce": "3d64ed55-ef6e-4bd5-99c9-677533babc22"
}
```
#### SUBSCRIBE
Used to subscribe to events. `evt` of the payload should be set to the event being subscribed to. `args` of the payload should be set to the args needed for the event.
###### Subscribe Response Structure
| Field | Type | Description |
| ----- | ------ | ---------------------------- |
| evt | string | event name now subscribed to |
###### Example Subscribe Command Payload
```json theme={"system"}
{
"nonce": "be9a6de3-31d0-4767-a8e9-4818c5690015",
"args": {
"guild_id": "199737254929760256"
},
"evt": "GUILD_STATUS",
"cmd": "SUBSCRIBE"
}
```
###### Example Subscribe Response Payload
```json theme={"system"}
{
"cmd": "SUBSCRIBE",
"data": {
"evt": "GUILD_STATUS"
},
"nonce": "be9a6de3-31d0-4767-a8e9-4818c5690015"
}
```
#### UNSUBSCRIBE
Used to unsubscribe from events. `evt` of the payload should be set to the event that was subscribed to. `args` of the payload should be set to the args needed for the previously subscribed event.
###### Unsubscribe Response Structure
| Field | Type | Description |
| ----- | ------ | -------------------------------- |
| evt | string | event name now unsubscribed from |
###### Example Unsubscribe Command Payload
```json theme={"system"}
{
"nonce": "647d814a-4cf8-4fbb-948f-898aad24f55b",
"args": {
"guild_id": "199737254929760256"
},
"evt": "GUILD_STATUS",
"cmd": "UNSUBSCRIBE"
}
```
###### Example Unsubscribe Response Payload
```json theme={"system"}
{
"cmd": "UNSUBSCRIBE",
"data": {
"evt": "GUILD_STATUS"
},
"nonce": "647d814a-4cf8-4fbb-948f-898aad24f55b"
}
```
#### SET\_CERTIFIED\_DEVICES
Used by hardware manufacturers to send information about the current state of their certified devices that are connected to Discord.
###### Set Certified Devices Argument Structure
| Field | Type | Description |
| ------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| devices | array of [certified device](/developers/topics/rpc#setcertifieddevices-device-object) objects | a list of devices for your manufacturer, in order of priority |
###### Device Object
| Field | Type | Description |
| --------------------------- | ------------------------------------------------------------------------- | -------------------------------------------------------- |
| type | [device type](/developers/topics/rpc#setcertifieddevices-device-type) | the type of device |
| id | string | the device's Windows UUID |
| vendor | [vendor](/developers/topics/rpc#setcertifieddevices-vendor-object) object | the hardware vendor |
| model | [model](/developers/topics/rpc#setcertifieddevices-model-object) object | the model of the product |
| related | array of strings | UUIDs of related devices |
| echo\_cancellation?\* | boolean | if the device's native echo cancellation is enabled |
| noise\_suppression?\* | boolean | if the device's native noise suppression is enabled |
| automatic\_gain\_control?\* | boolean | if the device's native automatic gain control is enabled |
| hardware\_mute?\* | boolean | if the device is hardware muted |
\*These fields are only applicable for `AUDIO_INPUT` device types
###### Vendor Object
| Field | Type | Description |
| ----- | ------ | ------------------ |
| name | string | name of the vendor |
| url | string | url for the vendor |
###### Model Object
| Field | Type | Description |
| ----- | ------ | ----------------- |
| name | string | name of the model |
| url | string | url for the model |
###### Device Type
| Type | Value |
| ------------- | ------------- |
| AUDIO\_INPUT | "audioinput" |
| AUDIO\_OUTPUT | "audiooutput" |
| VIDEO\_INPUT | "videoinput" |
###### Example Set Certified Devices Command Payload
```json theme={"system"}
{
"nonce": "9b4e9711-97f3-4f35-b047-32c82a51978e",
"cmd": "SET_CERTIFIED_DEVICES",
"args": {
"devices": [
{
"type": "audioinput",
"id": "aafc2003-da0e-42a3-b982-6a17a2812510",
"vendor": {
"name": "SteelSeries",
"url": "https://steelseries.com"
},
"model": {
"name": "Arctis 7",
"url": "https://steelseries.com/gaming-headsets/arctis-7"
},
"related": ["aafc2003-da0e-42a3-b982-6a17a2819999"],
"echo_cancellation": true,
"noise_suppression": true,
"automatic_gain_control": true,
"hardware_mute": false
}
]
}
}
```
###### Example Set Certified Devices Response Payload
```json theme={"system"}
{
"nonce": "9b4e9711-97f3-4f35-b047-32c82a51978e",
"cmd": "SET_CERTIFIED_DEVICES",
"data": null,
"evt": null
}
```
#### SET\_ACTIVITY
Used to update a user's Rich Presence.
###### Set Activity Argument Structure
When using `SET_ACTIVITY`, the `activity` object is limited to a `type` of Playing (`0`), Listening (`2`), Watching (`3`), or Competing (`5`).
| Field | Type | Description |
| -------- | -------------------------------------------------------------------- | --------------------------------------- |
| pid | integer | the application's process id |
| activity | [activity](/developers/events/gateway-events#activity-object) object | the rich presence to assign to the user |
###### Example Set Activity Payload
```json theme={"system"}
{
"cmd": "SET_ACTIVITY",
"args": {
"pid": 9999,
"activity": {
"state": "In a Group",
"state_url": "https://example.com/groups/50335231-9d9d-4ebd-873b-984787ee4d1d",
"details": "Competitive | In a Match",
"details_url": "https://example.com/matches/42340203-2f25-4534-8ff6-2a6509e81207",
"timestamps": {
"start": time(nullptr),
"end": time(nullptr) + (60 * 5 + 23)
},
"assets": {
"large_image": "numbani_map",
"large_text": "Numbani",
"large_url": "https://example.wiki/maps/Numbani",
"small_image": "pharah_profile",
"small_text": "Pharah",
"small_url": "https://example.wiki/characters/Pharah"
},
"party": {
"id": GameEngine.GetPartyId(),
"size": [3, 6]
},
"secrets": {
"join": "025ed05c71f639de8bfaa0d679d7c94b2fdce12f",
"spectate": "e7eb30d2ee025ed05c71ea495f770b76454ee4e0",
"match": "4b2fdce12f639de8bfa7e3591b71a0d679d7c93f"
},
"instance": true
}
},
"nonce": "647d814a-4cf8-4fbb-948f-898abd24f55b"
}
```
#### SEND\_ACTIVITY\_JOIN\_INVITE
Used to accept an Ask to Join request.
###### Send Activity Join Invite Argument Structure
| Field | Type | Description |
| -------- | --------- | ----------------------------- |
| user\_id | snowflake | the id of the requesting user |
###### Example Send Activity Join Invite Payload
```json theme={"system"}
{
"nonce": "5dc0c062-98c6-47a0-8922-15aerg126",
"cmd": "SEND_ACTIVITY_JOIN_INVITE",
"args": {
"user_id": "53908232506183680"
}
}
```
#### CLOSE\_ACTIVITY\_REQUEST
Used to reject an Ask to Join request.
###### Close Activity Request Argument Structure
| Field | Type | Description |
| -------- | --------- | ----------------------------- |
| user\_id | snowflake | the id of the requesting user |
###### Example Close Activity Request Payload
```json theme={"system"}
{
"nonce": "5dc0c062-98c6-47a0-8922-15aerg126",
"cmd": "CLOSE_ACTIVITY_REQUEST",
"args": {
"user_id": "53908232506183680"
}
}
```
#### READY
###### Ready Dispatch Data Structure
| Field | Type | Description |
| ------ | ----------------------------------------------------------------------------------------------- | ---------------------------------- |
| v | integer | RPC version |
| config | [rpc server configuration](/developers/topics/rpc#ready-rpc-server-configuration-object) object | server configuration |
| user | partial [user](/developers/resources/user#user-object) object | the user to whom you are connected |
###### RPC Server Configuration Object
| Field | Type | Description |
| ------------- | ------ | --------------------- |
| cdn\_host | string | server's cdn |
| api\_endpoint | string | server's api endpoint |
| environment | string | server's environment |
###### Example Ready Dispatch Payload
```json theme={"system"}
{
"cmd": "DISPATCH",
"data": {
"v": 1,
"config": {
"cdn_host": "cdn.discordapp.com",
"api_endpoint": "//discord.com/api",
"environment": "production"
},
"user": {
"id": "53908232506183680",
"username": "Mason",
"discriminator": "1337",
"avatar": null
}
},
"evt": "READY"
}
```
#### ERROR
###### Error Data Structure
| Field | Type | Description |
| ------- | ------- | ----------------- |
| code | integer | RPC Error Code |
| message | string | Error description |
###### Example Error Payload
```json theme={"system"}
{
"cmd": "AUTHORIZE",
"data": {
"code": 4007,
"message": "No client id provided"
},
"evt": "ERROR",
"nonce": "5102b6f0-c769-4f37-8cca-25fb0ab22628"
}
```
#### GUILD\_STATUS
###### Guild Status Argument Structure
| Field | Type | Description |
| --------- | ------ | ----------------------------------- |
| guild\_id | string | id of guild to listen to updates of |
###### Guild Status Dispatch Data Structure
| Field | Type | Description |
| ------ | ---------------------------------------------------------------- | ------------------------------------------------------ |
| guild | partial [guild](/developers/resources/guild#guild-object) object | guild with requested id |
| online | integer | number of online users in guild (deprecated; always 0) |
###### Example Guild Status Dispatch Payload
```json theme={"system"}
{
"cmd": "DISPATCH",
"data": {
"guild": {
"id": "199737254929760256",
"name": "test",
"icon_url": null
},
"online": 0
},
"evt": "GUILD_STATUS"
}
```
#### GUILD\_CREATE
No arguments
###### Guild Create Dispatch Data Structure
| Field | Type | Description |
| ----- | ------ | ----------------- |
| id | string | guild id |
| name | string | name of the guild |
###### Example Guild Create Dispatch Payload
```json theme={"system"}
{
"cmd": "DISPATCH",
"data": {
"id": "199737254929767562",
"name": "Test Server"
},
"evt": "GUILD_CREATE"
}
```
#### CHANNEL\_CREATE
No arguments
###### Channel Create Dispatch Data Structure
| Field | Type | Description |
| ----- | ------- | ---------------------------------------------------------------- |
| id | string | channel id |
| name | string | name of the channel |
| type | integer | channel type (guild text: 0, guild voice: 2, dm: 1, group dm: 3) |
###### Example Channel Create Dispatch Payload
```json theme={"system"}
{
"cmd": "DISPATCH",
"data": {
"id": "199737254929760257",
"name": "General",
"type": 0
},
"evt": "CHANNEL_CREATE"
}
```
#### VOICE\_CHANNEL\_SELECT
No arguments
###### Voice Channel Select Dispatch Data Structure
| Field | Type | Description |
| ----------- | ------ | ------------------------------ |
| channel\_id | string | id of channel (`null` if none) |
| guild\_id | string | id of guild (`null` if none) |
###### Example Voice Channel Select Dispatch Payload
```json theme={"system"}
{
"cmd": "DISPATCH",
"data": {
"channel_id": "199737254929760257",
"guild_id": "199737254929760256"
},
"evt": "VOICE_CHANNEL_SELECT"
}
```
#### VOICE\_SETTINGS\_UPDATE
###### Voice Settings Argument Structure
No arguments. Dispatches the [Get Voice Settings](/developers/topics/rpc#getvoicesettings) response.
###### Example Voice Settings Dispatch Payload
```json theme={"system"}
{
"cmd": "DISPATCH",
"data": {
"input": {
"available_devices": [
{
"id": "default",
"name": "Default"
},
{
"id": "Built-in Microphone",
"name": "Built-in Microphone"
}
],
"device_id": "default",
"volume": 49.803921580314636
},
"output": {
"available_devices": [
{
"id": "default",
"name": "Default"
},
{
"id": "Built-in Output",
"name": "Built-in Output"
}
],
"device_id": "default",
"volume": 93.00000071525574
},
"mode": {
"type": "VOICE_ACTIVITY",
"auto_threshold": true,
"threshold": -46.92622950819673,
"shortcut": [{ "type": 0, "code": 12, "name": "i" }],
"delay": 98.36065573770492
},
"automatic_gain_control": false,
"echo_cancellation": false,
"noise_suppression": false,
"qos": false,
"silence_warning": false
},
"evt": "VOICE_SETTINGS_UPDATE"
}
```
#### VOICE\_STATE\_CREATE/VOICE\_STATE\_UPDATE/VOICE\_STATE\_DELETE
Dispatches channel voice state objects
###### Voice State Argument Structure
| Field | Type | Description |
| ----------- | ------ | ------------------------------------- |
| channel\_id | string | id of channel to listen to updates of |
###### Example Voice State Dispatch Payload
```json theme={"system"}
{
"cmd": "DISPATCH",
"evt": "VOICE_STATE_CREATE",
"data": {
"voice_state": {
"mute": false,
"deaf": false,
"self_mute": false,
"self_deaf": false,
"suppress": false
},
"user": {
"id": "190320984123768832",
"username": "test 2",
"discriminator": "7479",
"avatar": "b004ec1740a63ca06ae2e14c5cee11f3",
"bot": false
},
"nick": "test user 2",
"volume": 110,
"mute": false,
"pan": {
"left": 1.0,
"right": 1.0
}
}
}
```
#### VOICE\_CONNECTION\_STATUS
No arguments
###### Voice Connection Status Dispatch Data Structure
| Field | Type | Description |
| ------------- | ----------------- | ----------------------------------------------- |
| state | string | one of the voice connection states listed below |
| hostname | string | hostname of the connected voice server |
| pings | array of integers | last 20 pings (in ms) |
| average\_ping | integer | average ping (in ms) |
| last\_ping | integer | last ping (in ms) |
###### Voice Connection States
| Field | Description |
| ------------------- | --------------------------------- |
| DISCONNECTED | TCP disconnected |
| AWAITING\_ENDPOINT | Waiting for voice endpoint |
| AUTHENTICATING | TCP authenticating |
| CONNECTING | TCP connecting |
| CONNECTED | TCP connected |
| VOICE\_DISCONNECTED | TCP connected, Voice disconnected |
| VOICE\_CONNECTING | TCP connected, Voice connecting |
| VOICE\_CONNECTED | TCP connected, Voice connected |
| NO\_ROUTE | No route to host |
| ICE\_CHECKING | WebRTC ice checking |
###### Example Voice Connection Status Dispatch Payload
```json theme={"system"}
{
"cmd": "DISPATCH",
"evt": "VOICE_CONNECTION_STATUS",
"data": {
"state": "VOICE_CONNECTED",
"hostname": "some-server.discord.gg",
"pings": [20, 13.37],
"average_ping": 13.37,
"last_ping": 20
}
}
```
#### MESSAGE\_CREATE/MESSAGE\_UPDATE/MESSAGE\_DELETE
Dispatches message objects, with the exception of deletions, which only contains the id in the message object.
###### Message Argument Structure
| Field | Type | Description |
| ----------- | ------ | ------------------------------------- |
| channel\_id | string | id of channel to listen to updates of |
###### Example Message Dispatch Payload
```json theme={"system"}
{
"cmd": "DISPATCH",
"data": {
"channel_id": "199737254929760256",
"message": {
"id": "199743874640379904",
"blocked": false,
"content": "test",
"content_parsed": [
{
"content": "test",
"type": "text"
}
],
"author_color": "#ffffff",
"edited_timestamp": null,
"timestamp": "2016-07-05T04:30:50.776Z",
"tts": false,
"mentions": [],
"mention_roles": [],
"mention_everyone": false,
"embeds": [],
"attachments": [],
"type": 0,
"pinned": false,
"author": {
"id": "190320984123768832",
"username": "test user 2",
"discriminator": "7479",
"avatar": "b004ec1740a63ca06ae2e14c5cee11f3",
"bot": false
}
}
},
"evt": "MESSAGE_CREATE"
}
```
#### SPEAKING\_START/SPEAKING\_STOP
###### Speaking Argument Structure
| Field | Type | Description |
| ----------- | ------ | ------------------------------------- |
| channel\_id | string | id of channel to listen to updates of |
###### Speaking Dispatch Data Structure
| Field | Type | Description |
| -------- | ------ | --------------------------------------- |
| user\_id | string | id of user who started/stopped speaking |
###### Example Speaking Dispatch Payload
```json theme={"system"}
{
"cmd": "DISPATCH",
"data": {
"user_id": "190320984123768832"
},
"evt": "SPEAKING_STOP"
}
```
#### NOTIFICATION\_CREATE
No arguments. This event requires the `rpc.notifications.read` [OAuth2 scope](/developers/topics/oauth2#shared-resources-oauth2-scopes).
###### Notification Create Dispatch Data Structure
| Field | Type | Description |
| ----------- | -------------------------------------------------------------- | ----------------------------------------- |
| channel\_id | string | id of channel where notification occurred |
| message | [message](/developers/resources/message#message-object) object | message that generated this notification |
| icon\_url | string | icon url of the notification |
| title | string | title of the notification |
| body | string | body of the notification |
###### Example Notification Create Dispatch Payload
```json theme={"system"}
{
"cmd": "DISPATCH",
"data": {
"channel_id": "199737254929760256",
"message": {
"id": "199743874640379904",
"blocked": false,
"content": "test",
"content_parsed": [
{
"content": "test",
"type": "text"
}
],
"author_color": "#ffffff",
"edited_timestamp": null,
"timestamp": "2016-07-05T04:30:50.776Z",
"tts": false,
"mentions": [],
"mention_roles": [],
"mention_everyone": false,
"embeds": [],
"attachments": [],
"type": 0,
"pinned": false,
"author": {
"id": "190320984123768832",
"username": "test user 2",
"discriminator": "7479",
"avatar": "b004ec1740a63ca06ae2e14c5cee11f3",
"bot": false
}
},
"icon_url": "https://cdn.discordapp.com/avatars/155607406007681024/8ab559b8286e48270c04471ae382cd9d.jpg",
"title": "test_user (#general)",
"body": "test message"
},
"evt": "NOTIFICATION_CREATE"
}
```
#### ACTIVITY\_JOIN
No arguments
###### Activity Join Dispatch Data Structure
| Field | Type | Description |
| ------ | ------ | ----------------------------------------------------------------------------------------------------- |
| secret | string | the [`join_secret`](/developers/developer-tools/game-sdk#activitysecrets-struct) for the given invite |
###### Example Activity Join Dispatch Payload
```json theme={"system"}
{
"cmd": "DISPATCH",
"data": {
"secret": "025ed05c71f639de8bfaa0d679d7c94b2fdce12f"
},
"evt": "ACTIVITY_JOIN"
}
```
#### ACTIVITY\_SPECTATE
No arguments
###### Activity Spectate Dispatch Data Structure
| Field | Type | Description |
| ------ | ------ | --------------------------------------------------------------------------------------------------------- |
| secret | string | the [`spectate_secret`](/developers/developer-tools/game-sdk#activitysecrets-struct) for the given invite |
###### Example Activity Spectate Dispatch Payload
```json theme={"system"}
{
"cmd": "DISPATCH",
"data": {
"secret": "e7eb30d2ee025ed05c71ea495f770b76454ee4e0"
},
"evt": "ACTIVITY_SPECTATE"
}
```
#### ACTIVITY\_JOIN\_REQUEST
No arguments
###### Activity Join Request Data Structure
| Field | Type | Description |
| ----- | ------------------------------------------------------------- | --------------------------------------------- |
| user | partial [user](/developers/resources/user#user-object) object | information about the user requesting to join |
###### Example Activity Join Request Dispatch Payload
```json theme={"system"}
{
"cmd": "DISPATCH",
"data": {
"user": {
"id": "53908232506183680",
"username": "Mason",
"discriminator": "1337",
"avatar": "a_bab14f271d565501444b2ca3be944b25"
}
},
"evt": "ACTIVITY_JOIN_REQUEST"
}
```
#### ACTIVITY\_INVITE
No arguments
###### Activity Invite Dispatch Data Structure
| Field | Type | Description |
| ----------- | -------------------------------------------------------------------- | ---------------------------------------- |
| type | integer | invite type; `1` for join |
| user | partial [user](/developers/resources/user#user-object) object | user who sent the invite |
| activity | [activity](/developers/events/gateway-events#activity-object) object | the activity associated with the invite |
| channel\_id | string | id of the channel the invite was sent in |
| message\_id | string | id of the invite message |
###### Example Activity Invite Dispatch Payload
```json theme={"system"}
{
"cmd": "DISPATCH",
"data": {
"type": 1,
"user": {
"id": "53908232506183680",
"username": "Mason",
"discriminator": "1337",
"avatar": "a_bab14f271d565501444b2ca3be944b25"
},
"activity": {
"application_id": "192741864418312192",
"name": "My Game",
"party": {
"id": "party1234",
"size": [2, 5]
}
},
"channel_id": "199737254929760256",
"message_id": "199743874640379904"
},
"evt": "ACTIVITY_INVITE"
}
```
#### CURRENT\_USER\_UPDATE
No arguments. Dispatches the current user's profile whenever it changes (avatar, username, etc.).
###### Current User Update Dispatch Data Structure
| Field | Type | Description |
| ------------------------ | ------- | ---------------------------------------------------------------------------------------------------------------------- |
| id | string | user's id |
| username | string | user's username |
| discriminator | string | user's discriminator |
| global\_name | string | user's display name |
| avatar | string | user's avatar hash |
| avatar\_decoration\_data | object | avatar decoration data, if any (`null` if none) |
| bot | boolean | whether the user is a bot |
| flags | integer | the public [flags](/developers/resources/user#user-object-user-flags) on a user's account |
| premium\_type | integer | type of [Nitro subscription](/developers/resources/user#user-object-premium-types). Requires `identify.premium` scope. |
###### Example Current User Update Dispatch Payload
```json theme={"system"}
{
"cmd": "DISPATCH",
"data": {
"id": "53908232506183680",
"username": "Mason",
"discriminator": "0",
"global_name": "Mason",
"avatar": "a_bab14f271d565501444b2ca3be944b25",
"avatar_decoration_data": null,
"bot": false,
"flags": 64,
"premium_type": 0
},
"evt": "CURRENT_USER_UPDATE"
}
```
#### RELATIONSHIP\_UPDATE
No arguments. Requires the `relationships_read` [OAuth2 scope](/developers/topics/oauth2#shared-resources-oauth2-scopes).
Fired when a relationship is added, updated (e.g. presence change), or removed. When a relationship is removed, `type` will be `0` (`NONE`).
###### Relationship Update Dispatch Data Structure
| Field | Type | Description |
| -------- | ---------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| type | integer | [relationship type](/developers/topics/rpc#relationshipupdate-relationship-types) |
| user | partial [user](/developers/resources/user#user-object) object | the related user |
| presence | [presence](/developers/topics/rpc#relationshipupdate-presence-object) object | the related user's current presence |
###### Relationship Types
| Type | Value | Description |
| ----------------- | ----- | ---------------------------------------- |
| NONE | 0 | relationship removed |
| FRIEND | 1 | user is a friend |
| BLOCKED | 2 | user is blocked |
| PENDING\_INCOMING | 3 | incoming friend request |
| PENDING\_OUTGOING | 4 | outgoing friend request |
| IMPLICIT | 5 | user is in a mutual guild (not a friend) |
###### Presence Object
| Field | Type | Description |
| -------- | -------------------------------------------------------------------- | ------------------------------------------------------------- |
| status | string | user's status (`online`, `idle`, `dnd`, `offline`) |
| activity | [activity](/developers/events/gateway-events#activity-object) object | user's current activity for this application (`null` if none) |
###### Example Relationship Update Dispatch Payload
```json theme={"system"}
{
"cmd": "DISPATCH",
"data": {
"type": 1,
"user": {
"id": "190320984123768832",
"username": "test user 2",
"discriminator": "0",
"global_name": "test user 2",
"avatar": "b004ec1740a63ca06ae2e14c5cee11f3",
"bot": false,
"flags": 0,
"premium_type": 0
},
"presence": {
"status": "online",
"activity": null
}
},
"evt": "RELATIONSHIP_UPDATE"
}
```
#### ENTITLEMENT\_CREATE
No arguments. Fired when the user acquires a new entitlement for this application.
###### Entitlement Create Dispatch Data Structure
| Field | Type | Description |
| ----------- | --------------------------------------------------------------------------------- | -------------------------------- |
| entitlement | [entitlement](/developers/topics/rpc#entitlementcreate-entitlement-object) object | the entitlement that was created |
###### Entitlement Object
| Field | Type | Description |
| --------------- | ------- | ------------------------------------------------------------------------------ |
| id | string | entitlement id |
| sku\_id | string | id of the SKU this entitlement is for |
| application\_id | string | id of the application |
| user\_id | string | id of the user that owns the entitlement |
| type | integer | [entitlement type](/developers/topics/rpc#entitlementcreate-entitlement-types) |
| deleted | boolean | whether the entitlement has been deleted |
| starts\_at? | ISO8601 | start date of the entitlement |
| ends\_at? | ISO8601 | end date of the entitlement |
| guild\_id? | string | id of the guild the entitlement applies to |
| consumed? | boolean | for consumable entitlements, whether the entitlement has been consumed |
###### Entitlement Types
| Type | Value | Description |
| ------------------------- | ----- | ------------------------------ |
| PURCHASE | 1 | purchased by a user |
| PREMIUM\_SUBSCRIPTION | 2 | a Nitro subscription |
| DEVELOPER\_GIFT | 3 | gifted by a developer |
| TEST\_MODE\_PURCHASE | 4 | purchased in test mode |
| FREE\_PURCHASE | 5 | granted for free |
| USER\_GIFT | 6 | gifted by another user |
| PREMIUM\_PURCHASE | 7 | purchased as a premium feature |
| APPLICATION\_SUBSCRIPTION | 8 | an app subscription |
###### Example Entitlement Create Dispatch Payload
```json theme={"system"}
{
"cmd": "DISPATCH",
"data": {
"entitlement": {
"id": "1019653849998299136",
"sku_id": "1019475255913222144",
"application_id": "192741864418312192",
"user_id": "53908232506183680",
"type": 8,
"deleted": false,
"starts_at": "2022-09-14T17:00:18.704163+00:00",
"ends_at": "2022-10-14T17:00:18.704163+00:00"
}
},
"evt": "ENTITLEMENT_CREATE"
}
```
#### ENTITLEMENT\_DELETE
No arguments. Fired when an entitlement for this application is removed. The entitlement object in the payload reflects the state of the entitlement at the time of deletion.
###### Entitlement Delete Dispatch Data Structure
| Field | Type | Description |
| ----------- | --------------------------------------------------------------------------------- | -------------------------------- |
| entitlement | [entitlement](/developers/topics/rpc#entitlementcreate-entitlement-object) object | the entitlement that was deleted |
###### Example Entitlement Delete Dispatch Payload
```json theme={"system"}
{
"cmd": "DISPATCH",
"data": {
"entitlement": {
"id": "1019653849998299136",
"sku_id": "1019475255913222144",
"application_id": "192741864418312192",
"user_id": "53908232506183680",
"type": 8,
"deleted": true,
"starts_at": "2022-09-14T17:00:18.704163+00:00",
"ends_at": "2022-10-14T17:00:18.704163+00:00"
}
},
"evt": "ENTITLEMENT_DELETE"
}
```
# Managing Your Developer Team
Source: https://docs.discord.com/developers/topics/teams
Learn how to create and manage developer teams for collaborative development.
Teams are groups of developers (or other Discord users) who want to collaborate and share access to an app's configuration, management, and payout settings. Go team(s)!
## Creating a Team
To create or be a member on a team, you must [enable 2FA for your Discord account](https://support.discord.com/hc/en-us/articles/219576828-Setting-up-Two-Factor-Authentication). After you have 2FA enabled, create a team by navigating to the [Teams page](https://discord.com/developers/teams) then clicking the "New Team" button.
Once you create a team, you'll land on the **Team Information** page where you can fill out details and start inviting other Discord users to join your team. Since users added to a team have access to any apps that team owns, use caution when adding new team members.
Only the team Owner and team Admins can invite or remove additional users.
## Adding Apps to a Team
Once your team is set up, you can create or transfer apps that will be owned by the team. Teams can have a maximum of 75 apps.
### Creating an App
To create a new app that belongs to a team, select the team from the **Team** dropdown in the app creation modal. If you want to keep the app under your own account's ownership, choose `Personal`:
### Transferring an App
To transfer an existing app to a team, navigate to the [Application](https://discord.com/developers/applications) that you want to transfer. At the bottom of the app's [**General Information** page](https://discord.com/developers/applications/select/information), click "Transfer App to Team".
Once an app has been transferred to a team, it *cannot* be transferred back.
## Team Member Roles
Team members can be one of four roles (owner, admin, developer, and read-only), and each role inherits the access of those below it. Roles for team members can be configured under **Team Members** in a team's settings.
###### Team Member Role Types
| Role Name | Value | Description |
| --------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Owner\* | | Owners are the most permissible role, and can take destructive, irreversible actions like deleting team-owned apps or the team itself. Teams are limited to 1 owner. |
| Admin | admin | Admins have similar access as owners, except they cannot take destructive actions on the team or team-owned apps. |
| Developer | developer | Developers can access information about team-owned apps, like the client secret or public key. They can also take limited actions on team-owned apps, like configuring interaction endpoints or resetting the bot token. Members with the Developer role *cannot* manage the team or its members, or take destructive actions on team-owned apps. |
| Read-only | read\_only | Read-only members can access information about a team and any team-owned apps. Some examples include getting the IDs of applications and exporting payout records. Members can also invite bots associated with team-owned apps that are marked private. |
\* The owner role is not represented in the `role` field on the [team member object](/developers/topics/teams#data-models-team-member-object). Instead, the `owner_user_id` field on the [team object](/developers/topics/teams#data-models-team-object) should be used to identify which user has the owner role for the team.
## Data Models
###### Team Object
| field | type | description |
| --------------- | --------------------------------------------------------------------------------------- | ------------------------------------ |
| icon | ?string | Hash of the image of the team's icon |
| id | snowflake | Unique ID of the team |
| members | array of [team member](/developers/topics/teams#data-models-team-member-object) objects | Members of the team |
| name | string | Name of the team |
| owner\_user\_id | snowflake | User ID of the current team owner |
###### Team Member Object
| field | type | description |
| ----------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| membership\_state | integer | User's [membership state](/developers/topics/teams#data-models-membership-state-enum) on the team |
| team\_id | snowflake | ID of the parent team of which they are a member |
| user | partial [user](/developers/resources/user#user-object) object | Avatar, discriminator, ID, and username of the user |
| role | string | [Role](/developers/topics/teams#team-member-roles-team-member-role-types) of the team member |
###### Membership State Enum
| name | value |
| -------- | ----- |
| INVITED | 1 |
| ACCEPTED | 2 |
# Threads
Source: https://docs.discord.com/developers/topics/threads
Learn about Discord threads - temporary sub-channels for organizing conversations.
[Threads](/developers/resources/channel#channel-object) can be thought of as temporary sub-channels inside an existing channel, to help better organize conversation in a busy channel.
Threads have been designed to be very similar to [channel](/developers/resources/channel#channel-object) objects, and this topic aggregates all of the information about threads, which should all help to make migrating very straightforward.
## Backwards Compatibility
Threads are only available in API v9 and above. Bots that do not update to API v9 or above will not receive most gateway events for threads, or things that happen in threads (such as [Message Create](/developers/events/gateway-events#message-create)). Bots on API v8 will still receive gateway events for Interactions.
The list of gateway events that may be dropped includes, but is not limited to:
* MESSAGE\_CREATE
* MESSAGE\_DELETE
* MESSAGE\_DELETE\_BULK
* MESSAGE\_REACTION\_ADD
* MESSAGE\_REACTION\_REMOVE
* MESSAGE\_REACTION\_REMOVE\_ALL
* MESSAGE\_REACTION\_REMOVE\_EMOJI
* MESSAGE\_UPDATE
* THREAD\_CREATE
* THREAD\_UPDATE
* THREAD\_DELETE
* THREAD\_MEMBER\_UPDATE
* THREAD\_MEMBERS\_UPDATE
## Thread Fields
Threads share and repurpose a number of the existing fields from the [channel object](/developers/resources/channel#channel-object):
* `id`, `guild_id`, `type`, `name`, `last_message_id`, `last_pin_timestamp`, `rate_limit_per_user` are being re-used
* `owner_id` has been repurposed to store the id of the user that started the thread
* `parent_id` has been repurposed to store the id of the `GUILD_TEXT` or `GUILD_ANNOUNCEMENT` channel the thread was created in
Additionally, there are a few fields that are only available on threads:
* `member_count` stores an approximate member count, but it stops counting at 50 (this is only used in our UI, so it is not valuable to bots)
* `message_count` and `total_message_sent` store the number of messages in a thread. The difference is that when a message is deleted, `message_count` is decremented, but `total_message_sent` will not be. (threads created before July 1, 2022 stop counting both values at 50).
* `thread_metadata` contains a few thread specific fields, `archived`, `archive_timestamp`, `auto_archive_duration`, `locked`. `archive_timestamp` is changed when creating, archiving, or unarchiving a thread, and when changing the `auto_archive_duration` field.
## Public & Private Threads
Public threads are viewable by everyone who can view the parent channel of the thread. Public threads must be created from an existing message, but can be "orphaned" if that message is deleted. The created thread and the message it was started from will share the same id. The [type](/developers/resources/channel#channel-object-channel-types) of thread created matches the [type](/developers/resources/channel#channel-object-channel-types) of the parent channel. `GUILD_TEXT` channels [create](/developers/resources/channel#start-thread-from-message) `PUBLIC_THREAD` and `GUILD_ANNOUNCEMENT` channels [create](/developers/resources/channel#start-thread-from-message) `ANNOUNCEMENT_THREAD`.
Private threads behave similar to Group DMs, but in a Guild. Private threads are always [created](/developers/resources/channel#start-thread-without-message) with the `GUILD_PRIVATE_THREAD` [type](/developers/resources/channel#channel-object-channel-types) and can only be created in `GUILD_TEXT` channels.
## Locked Threads
Users (including bot users) without the `MANAGE_THREADS` permission are more restricted in locked threads. Users won't be able to create or update messages in locked threads, or update properties like its title or tags. Additionally, some user activity like deleting messages and adding or removing reactions will *only* be allowed in locked threads if that thread is also active (or un-archived).
If a user or bot user has the `MANAGE_THREADS` permission, they will still be able to make changes to the thread and messages.
## Active & Archived Threads
Every thread can be either active or archived. Changing a thread from archived -> active is referred to as unarchiving the thread. Threads that have `locked` set to true can only be unarchived by a user with the `MANAGE_THREADS` permission.
Besides helping to de-clutter the UI for users, archiving exists to limit the working set of threads that need to be kept around. Since the number of archived threads can be quite large, keeping all of them in memory may be quite prohibitive. Therefore guilds are capped at a certain number of active threads, and only active threads can be manipulated. Users cannot edit messages, add reactions, use application commands, or join archived threads. The only operation that should happen within an archived thread is messages being deleted. Sending a message will automatically unarchive the thread, unless the thread has been locked by a moderator.
Because of this constraint, the gateway protocol is designed to ensure that bots are able to have an accurate view of the full set of active threads, but archived threads are not synced up-front via the gateway.
Threads do not count against the max-channels limit in a guild, but there is a limit on the maximum number of active threads in a guild.
Threads automatically archive after a period of inactivity. As a server approaches the max thread limit this timer will automatically lower, usually not below the `auto_archive_duration`. In very busy channels, threads set to a 7 day auto archive may archive earlier to help avoid the server becoming "full"). "Activity" is defined as sending a message, unarchiving a thread, or changing the auto-archive time. The `auto_archive_duration` field previously controlled how long a thread could stay active, but is now repurposed to control how long the thread stays in the channel list. Channels can also set `default_auto_archive_duration`, which is used by our clients to pre-select a different `auto_archive_duration` value when a user creates a thread.
## Permissions
Threads generally inherit permissions from the parent channel (e.g. if you can add reactions in the parent channel, you can do that in a thread as well).
Three permission bits are specific to threads: `CREATE_PUBLIC_THREADS`, `CREATE_PRIVATE_THREADS`, and `SEND_MESSAGES_IN_THREADS`.
The `SEND_MESSAGES` permission has no effect in threads; users must have `SEND_MESSAGES_IN_THREADS` to talk in a thread.
Private threads are similar to Group DMs, but in a guild: You must be invited to the thread to be able to view or participate in it, or be a moderator (`MANAGE_THREADS` permission).
Finally, threads are treated slightly differently from channels in the gateway protocol. Clients will not be informed of a thread through the gateway if they do not have permission to view that thread.
## Gateway Events
* [Guild Create](/developers/events/gateway-events#guild-create) contains a field, `threads`, which is an array of channel objects. This represents all active threads in the guild that the current user is able to view.
* When a thread is created, updated, or deleted, a [Thread Create](/developers/events/gateway-events#thread-create), [Thread Update](/developers/events/gateway-events#thread-update), or [Thread Delete](/developers/events/gateway-events#thread-delete) event is sent. Like their channel counterparts, these just contain a thread.
* Since the gateway only syncs active threads that the user can see, if a user *gains* access to a channel, then the gateway may need to sync the active threads in that channel to the user. It will send a [Thread List Sync](/developers/events/gateway-events#thread-list-sync) event for this.
## Thread Membership
Each thread tracks explicit membership. There are two primary use cases for this data:
* Clients use *their own* [thread member](/developers/resources/channel#thread-member-object) to calculate read states and notification settings. This is largely irrelevant for bots though, but is the reason for some of the syncing complexity detailed here.
* Knowing everyone that is in a thread.
Membership is tracked in an array of [thread member](/developers/resources/channel#thread-member-object) objects. These have four fields, `id` (the thread id), `user_id`, `join_timestamp`, and `flags`. Currently the only `flags` are for notification settings, but others may be added in future updates.
### Syncing for the current user
* A [Thread Members Update](/developers/events/gateway-events#thread-members-update) Gateway Event is always sent when the current user is added to or removed from a thread.
* A [Thread Member Update](/developers/events/gateway-events#thread-member-update) Gateway Event is sent whenever the current user's [thread member](/developers/resources/channel#thread-member-object) object is updated.
* Certain API calls, such as listing archived threads and search will return an array of [thread member](/developers/resources/channel#thread-member-object) objects for any returned threads the current user is a member of. Other API calls, such as getting a channel will return the [thread member](/developers/resources/channel#thread-member-object) object for the current user as a property on the channel, if the current user is a member of the thread.
* The [Guild Create](/developers/events/gateway-events#guild-create) Gateway Event will contain a [thread member](/developers/resources/channel#thread-member-object) object as a property on any returned threads the current is a member of.
* The [Thread Create](/developers/events/gateway-events#thread-create) Gateway Event will contain a [thread member](/developers/resources/channel#thread-member-object) object as a property of the thread if the current user is a member of, and the user has recently gained access to view the parent channel.
* The [Thread List Sync](/developers/events/gateway-events#thread-list-sync) Gateway Event will contain an array of [thread member](/developers/resources/channel#thread-member-object) objects for any returned threads the current user is a member of.
### Syncing for other users
These require the `GUILD_MEMBERS` [Gateway Intent](/developers/events/gateway#gateway-intents)
* An API `GET` call to [`/channels//thread-members`](/developers/resources/channel#list-thread-members) which returns an array of [thread member](/developers/resources/channel#thread-member-object) objects.
* The [Thread Members Update](/developers/events/gateway-events#thread-members-update) Gateway Event which will include all users who were added to or removed from a thread by an action.
## Editing & Deleting Threads
Threads can be edited and deleted with the existing `PATCH` and `DELETE` endpoints to edit a channel.
* Deleting a thread requires the `MANAGE_THREADS` permission.
* Editing a thread to set `archived` to `false` only requires the current user has already been added to the thread. If `locked` is true, then the user must have created the thread or have `MANAGE_THREADS`
* Editing a thread to change the `name`, `archived`, `auto_archive_duration` fields requires `MANAGE_THREADS` or that the current user is the thread creator
* Editing a thread to change `rate_limit_per_user` or `locked` requires `MANAGE_THREADS`
## Age-Restricted Threads
Threads do not explicitly set the `nsfw` field. All threads in an [age-restricted channel](https://support.discord.com/hc/en-us/articles/115000084051) inherit that setting though.
## New Message Types
Threads introduce a few [message types](/developers/resources/message#message-object-message-types), and repurpose some others:
* `RECIPIENT_ADD` and `RECIPIENT_REMOVE` have been repurposed to also send when a user is added to or removed from a thread by someone else
* `CHANNEL_NAME_CHANGE` has been repurposed and is sent when the thread's name is changed
* `THREAD_CREATED` is a new message sent to the parent `GUILD_TEXT` channel, used to inform users that a thread has been created. It is currently only sent in one case: when a `PUBLIC_THREAD` is created from an older message (older is still TBD, but is currently set to a very small value). The message contains a [message reference](/developers/resources/message#message-reference-structure) with the `guild_id` and `channel_id` of the thread. The `content` of the message is the `name` of the thread.
* `THREAD_STARTER_MESSAGE` is a new message sent as the first message in threads that are started from an existing message in the parent channel. It *only* contains a [message reference](/developers/resources/message#message-reference-structure) field that points to the message from which the thread was started.
## Enumerating threads
There are four `GET` routes for enumerating threads in a specific channel:
* [`/guilds//threads/active`](/developers/resources/guild#list-active-guild-threads) returns all active threads in a guild that the current user can access, includes public & private threads
* [`/channels//users/@me/threads/archived/private`](/developers/resources/channel#list-joined-private-archived-threads) returns all archived, private threads in a channel, that the current user has is a member of, sorted by thread id descending
* [`/channels//threads/archived/public`](/developers/resources/channel#list-public-archived-threads) returns all archived, public threads in a channel, sorted by archive timestamp descending
* [`/channels//threads/archived/private`](/developers/resources/channel#list-private-archived-threads) returns all archived, private threads in a channel, sorted by archive timestamp descending
## Webhooks
Webhooks can send messages to threads by using the `thread_id` query parameter. See the [execute webhook](/developers/resources/webhook#execute-webhook) docs for more details.
## Details about Thread Access and Syncing
While the syncing of threads is similar to channels, there are two important differences that are relevant for [Thread List Sync](/developers/events/gateway-events#thread-list-sync) and [Thread Create](/developers/events/gateway-events#thread-create) events:
1. The [Gateway](/developers/events/gateway) will only sync threads that the app has permission to view
2. The Gateway will only sync threads once the app has "subscribed" to the guild. For context, in Discord's official clients, a subscription happens when the user visits a channel in the guild.
These differences mean there is some unique behavior that is worth going into.
### Thread Access
#### Gaining Access to Private Threads
When an app is added to a private thread, it likely doesn't have that thread in memory yet since it doesn't have permission to view it.
Private threads are only synced to you if you are a member or a moderator. Whenever a user is added to a private thread, the Gateway also sends a [Thread Create](/developers/events/gateway-events#thread-create) event. This ensures the client always has a non-null value for that thread.
The `THREAD_CREATE` event is also sent when the user is a moderator (and thus would already have the channel in memory).
#### Gaining Access to Public Threads
Upon connecting to the Gateway, apps will be automatically subscribed to thread events and active threads.
However, when a non-app is added to a public thread but hasn't subscribed to threads, it may not have that thread in memory yet (which is a requirement for Discord's clients). Because of this, the Gateway will send a [Thread Create](/developers/events/gateway-events#thread-create) event when a user is added to *any* thread, even if the event is not necessary for apps.
### Channel Access
#### Gaining Access to Channels
When an app gains access to a channel (for example, they're given the moderator role), they likely won't have the threads in memory for that channel since the Gateway only syncs threads that the client has permission to view. To account for this, a [Thread List Sync](/developers/events/gateway-events#thread-list-sync) event is sent.
The [Thread List Sync](/developers/events/gateway-events#thread-list-sync) event contains a `channel_ids` array, which is the IDs of all channels whose threads are being synced. This field can be used to first clear out any active threads whose `parent_id` is in the `channel_ids` array, and then ingest any threads that were in the event.
#### Losing Access to Channels
When an app loses access to a channel, the Gateway does **not** send it [Thread Delete](/developers/events/gateway-events#thread-delete) event (or any equivalent thread-specific event). Instead, the app will receive the event that caused its permissions on the channel to change.
If an app wanted to track when it lost access to any thread, it's possible but difficult as it would need to handle all cases correctly. Usually, events that cause permission changes are a [Guild Role Update](/developers/events/gateway-events#guild-role-update), [Guild Member Update](/developers/events/gateway-events#guild-member-update) or [Channel Update](/developers/events/gateway-events#channel-update) event.
Discord's clients check their permissions *first* when performing an action. That way, even if it has some stale data, it does not end up acting on it.
Additionally, when a user or app loses access to a channel, they are not removed from the thread and will continue to be reported as a member of that thread. However, they will **not** receive any new Gateway events unless they are removed from the thread, in which case they will receive a [Thread Members Update](/developers/events/gateway-events#thread-members-update) event.
### Unarchiving a Thread
When a thread is unarchived, there is no guarantee that an app has the thread or its member status in memory. To account for this, the Gateway will send two events (in the listed order):
1. A [Thread Update](/developers/events/gateway-events#thread-update) event, which contains the full channel object.
2. A [Thread Member Update](/developers/events/gateway-events#thread-member-update) event, which is sent to all members of the unarchived thread. Discord's clients only load active threads into memory on start, so this event is sent even if it may not be relevant to most apps.
## Forums
A `GUILD_FORUM` (type `15`) channel is similar to a `GUILD_TEXT` channel, except *only* threads can be created in them. Unless otherwise noted, threads in forum channels behave in the same way as in text channels—meaning they use the same endpoints and receive the same Gateway events.
Messages cannot be sent directly in forum channels.
More information about forum channels and how they appear in Discord can be found in the [Forum Channels FAQ](https://support.discord.com/hc/en-us/articles/6208479917079-Forum-Channels-FAQ#h_01G69FJQWTWN88HFEHK7Z6X79N)
## Media Channels
A `GUILD_MEDIA` (type `16`) channel is similar to a `GUILD_FORUM` channel in that only threads can be created in them. Unless otherwise noted, threads in media channels behave in the same way as in forum channels - meaning they use the same endpoints and receive the same Gateway events. More information about media channels and how they appear in Discord can be found in the [media channels Help Center Article](https://creator-support.discord.com/hc/en-us/articles/14346342766743).
`GUILD_MEDIA` channels are in beta and still being actively developed. The API and other technical details are subject to change.
### Creating Threads in Forum and Media Channels
Within thread-only channels, threads appear as posts. Threads can be created using the [`POST /channels//threads`](/developers/resources/channel#start-thread-in-forum-or-media-channel) endpoint with [slightly different parameters](/developers/resources/channel#start-thread-in-forum-or-media-channel-jsonform-params) than threads in text channels. For example, when creating threads in a threads-only channel, a message is created that has the same ID as the thread which requires you to pass parameters for both a thread *and* a message.
Threads in thread-only channels have the same permissions behavior as threads in a text channel, inheriting all permissions from the parent channel, with one exception: creating a thread in a thread-only channel only requires the `SEND_MESSAGES` permission.
### Forum and Media Channel Fields
It's worth calling out a few details about fields specific to thread-only channels that may be important to keep in mind:
* The `last_message_id` field is the ID of the most recently created thread in that channel. As with messages, your app will not receive a `CHANNEL_UPDATE` event when the field is changed. Instead clients should update the value when receiving [Thread Create](/developers/events/gateway-events#thread-create) events.
* The `topic` field is what is shown in the "Guidelines" section within the Discord client.
* The `rate_limit_per_user` field limits how frequently threads can be created. There is a new `default_thread_rate_limit_per_user` field on thread-only channels as well, which limits how often messages can be sent *in a thread*. This field is copied into `rate_limit_per_user` on the thread at creation time.
* The `available_tags` field can be set when creating or updating a channel, which determines which tags can be set on individual threads within the thread's `applied_tags` field.
* The `flags` field indicates any [channel flags](/developers/resources/channel#channel-object-channel-flags) set for a thread-only channel. Currently only `REQUIRE_TAG` can be used, which requires that a tag from `available_tags` be specified when creating a thread in that channel.
All fields for channels, including thread-only channels, can be found in the [Channel Object](/developers/resources/channel#channel-object).
### Forum and Media Channel Thread Fields
A thread can be pinned within a thread-only, which will be represented as the [`PINNED` flag](/developers/resources/channel#channel-object-channel-flags) in the `flags` field within a thread-only channel. A thread that is pinned will have the `(1 << 1)` flag set, and archiving that thread will unset the flag. A pinned thread will *not* auto-archive.
The `message_count` and `total_message_sent` fields on threads in thread-only channels will increment on `MESSAGE_CREATE` events, and decrement on `MESSAGE_DELETE` and `MESSAGE_DELETE_BULK` events. There will be no specific `CHANNEL_UPDATE` event that notifies your app of changes to those fields—instead, apps should update those values when receiving corresponding events.
All fields for threads in thread-only channels can be found in the [channel resources documentation](/developers/resources/channel#start-thread-in-forum-or-media-channel-jsonform-params).
### Forum and Media Channel Formatting
In thread-only channels, the first message in a thread and the channel topic can both contain markdown for bulleted lists and headings (unlike text channels).
# Voice
Source: https://docs.discord.com/developers/topics/voice-connections
Technical guide to implementing voice connections with Discord.
Voice connections operate in a similar fashion to the [Gateway](/developers/events/gateway) connection. However, they use a different set of payloads and a separate UDP-based connection for voice data transmission. Because UDP is used for both receiving and transmitting voice data, your client *must* be able to receive UDP packets, even through a firewall or NAT (see [UDP Hole Punching](https://en.wikipedia.org/wiki/UDP_hole_punching) for more information). The Discord Voice servers implement functionality (see [IP Discovery](/developers/topics/voice-connections#ip-discovery)) for discovering the local machines remote UDP IP/Port, which can assist in some network configurations.
## Voice Gateway Versioning
To ensure that you have the most up-to-date information, please use [version 8](/developers/topics/voice-connections#voice-gateway-versioning-gateway-versions). Otherwise, we cannot guarantee that the [Opcodes](/developers/topics/opcodes-and-status-codes#voice) documented here will reflect what you receive over the socket.
Previously if the version query string `v` was omitted the voice gateway would default to version 1. That behavior is being deprecated, in the future you must present a voice gateway version or your voice connection will be rejected.
Versions below 4 as well as the default version behavior will be discontinued as of November 18th, 2024. Connections without a version or with a version less than version 4 will be rejected as of this date.
###### Gateway Versions
| Version | Status | WebSocket URL Append | Change |
| ------- | ----------- | -------------------- | ---------------------------------------------------------------------- |
| 8 | recommended | ?v=8 | Added server message buffering, missed messages re-delivered on resume |
| 7 | available | ?v=7 | Added channel options opcode |
| 6 | available | ?v=6 | Added code version opcode |
| 5 | available | ?v=5 | Added video sink wants opcode |
| 4 | available | ?v=4 | Changed speaking status to bitmask from boolean |
| 3 | deprecated | ?v=3 | Added video |
| 2 | deprecated | ?v=2 | Changed heartbeat reply from server to heartbeat ACK opcode |
| 1 | deprecated | ?v=1 | Initial version |
## Connecting to Voice
### Retrieving Voice Server Information
The first step in connecting to a voice server (and in turn, a guild's voice channel) is formulating a request that can be sent to the [Gateway](/developers/events/gateway), which will return information about the voice server we will connect to. Because Discord's voice platform is widely distributed, users **should never** cache or save the results of this call. To inform the gateway of our intent to establish voice connectivity, we first send an [Opcode 4 Gateway Voice State Update](/developers/topics/opcodes-and-status-codes#gateway):
###### Gateway Voice State Update Example
```json theme={"system"}
{
"op": 4,
"d": {
"guild_id": "41771983423143937",
"channel_id": "127121515262115840",
"self_mute": false,
"self_deaf": false
}
}
```
If our request succeeded, the gateway will respond with *two* events—a [Voice State Update](/developers/events/gateway-events#voice-state-update) event and a [Voice Server Update](/developers/events/gateway-events#voice-server-update) event—meaning your library must properly wait for both events before continuing. The first will contain a new key, `session_id`, and the second will provide voice server information we can use to establish a new voice connection:
###### Example Voice Server Update Payload
```json theme={"system"}
{
"t": "VOICE_SERVER_UPDATE",
"s": 2,
"op": 0,
"d": {
"token": "my_token",
"guild_id": "41771983423143937",
"endpoint": "sweetwater-12345.discord.media:2048"
}
}
```
With this information, we can move on to establishing a voice WebSocket connection.
When sending a voice state update to change channels within the same guild, it is possible to receive a VOICE\_SERVER\_UPDATE with the same `endpoint` as the previous VOICE\_SERVER\_UPDATE. The `token` will be changed and you cannot re-use the previous session during a channel change, even if the endpoint remains the same.
Bot users respect the voice channel's user limit, if set. When the voice channel is full, you will not receive the Voice State Update or Voice Server Update events in response to your own Voice State Update. Having `MOVE_MEMBERS` permission bypasses this limit and allows you to join regardless of the channel being full or not.
## Establishing a Voice Websocket Connection
Once we retrieve a session\_id, token, and endpoint information, we can connect and handshake with the voice server over another secure WebSocket. Unlike the gateway endpoint we receive in an HTTP [Get Gateway](/developers/events/gateway#get-gateway) request, the endpoint received from our [Voice Server Update](/developers/events/gateway-events#voice-server-update) payload does not contain a URL protocol, so some libraries may require manually prepending it with "wss\://" before connecting. Once connected to the voice WebSocket endpoint, we can send an [Opcode 0 Identify](/developers/topics/opcodes-and-status-codes#voice) payload with our server\_id, user\_id, session\_id, and token:
###### Example Voice Identify Payload
```json theme={"system"}
{
"op": 0,
"d": {
"server_id": "41771983423143937",
"user_id": "104694319306248192",
"session_id": "my_session_id",
"token": "my_token",
"max_dave_protocol_version": 1
}
}
```
The voice server should respond with an [Opcode 2 Ready](/developers/topics/opcodes-and-status-codes#voice) payload, which informs us of the `SSRC`, UDP IP/port, and supported encryption modes the voice server expects:
###### Example Voice Ready Payload
```json theme={"system"}
{
"op": 2,
"d": {
"ssrc": 1,
"ip": "127.0.0.1",
"port": 1234,
"modes": ["xsalsa20_poly1305", "xsalsa20_poly1305_suffix", "xsalsa20_poly1305_lite"],
"heartbeat_interval": 1
}
}
```
`heartbeat_interval` here is an erroneous field and should be ignored. The correct `heartbeat_interval` value comes from the Hello payload.
## Heartbeating
In order to maintain your WebSocket connection, you need to continuously send heartbeats at the interval determined in [Opcode 8 Hello](/developers/topics/opcodes-and-status-codes#voice):
###### Example Hello Payload
```json theme={"system"}
{
"op": 8,
"d": {
"heartbeat_interval": 41250,
}
}
```
After receiving [Opcode 8 Hello](/developers/topics/opcodes-and-status-codes#voice), you should send [Opcode 3 Heartbeat](/developers/topics/opcodes-and-status-codes#voice)—which contains an integer nonce—every elapsed interval:
###### Example Heartbeat Payload since V8
```json theme={"system"}
{
"op": 3,
"d": {
"t": 1501184119561,
"seq_ack": 10
}
}
```
Since voice gateway version 8, heartbeat messages must include `seq_ack` which contains the sequence number of last numbered message received from the gateway. See [Buffered Resume](/developers/topics/voice-connections#buffered-resume) for more information.
###### Example Heartbeat Payload below V8
```json theme={"system"}
{
"op": 3,
"d": 1501184119561
}
```
In return, you will be sent back an [Opcode 6 Heartbeat ACK](/developers/topics/opcodes-and-status-codes#voice) that contains the previously sent nonce:
###### Example Heartbeat ACK Payload since V8
```json theme={"system"}
{
"op": 6,
"d": {
"t": 1501184119561
}
}
```
###### Example Heartbeat ACK Payload below V8
```json theme={"system"}
{
"op": 6,
"d": 1501184119561
}
```
## Establishing a Voice UDP Connection
Once we receive the properties of a UDP voice server from our [Opcode 2 Ready](/developers/topics/opcodes-and-status-codes#voice) payload, we can proceed to the final step of voice connections, which entails establishing and handshaking a UDP connection for voice data.
###### Example Ready Payload
```json theme={"system"}
{
"op": 2,
"d": {
...,
"modes": ["aead_aes256_gcm_rtpsize", "aead_xchacha20_poly1305_rtpsize", "xsalsa20_poly1305_lite_rtpsize"]
}
}
```
First, we open a UDP connection to the IP and port provided in the Ready payload. If required, we can now perform an [IP Discovery](/developers/topics/voice-connections#ip-discovery) using this connection. Once we've fully discovered our external IP and UDP port, we can then tell the voice WebSocket what it is, and start receiving/sending data. We do this using [Opcode 1 Select Protocol](/developers/topics/opcodes-and-status-codes#voice):
###### Example Select Protocol Payload
```json theme={"system"}
{
"op": 1,
"d": {
"protocol": "udp",
"data": {
"address": "127.0.0.1",
"port": 1337,
"mode": "aead_aes256_gcm_rtpsize"
}
}
}
```
## End-to-End Encryption (DAVE Protocol)
Since September 2024, Discord is migrating voice and video in DMs, Group DMs, voice channels, and Go Live streams to use end-to-end encryption (E2EE).
To support our long-term privacy goals, we will **only support E2EE calls starting on March 1st, 2026** for all audio
and video conversations in direct messages (DMs), group messages (GDMs), voice channels, and Go Live streams on
Discord.
We highly recommend you implement DAVE support as soon as possible to ensure a smooth transition.
This section is a high-level overview of how to support Discord's audio & video end-to-end encryption (DAVE) protocol, centered around the voice gateway opcodes necessary for the protocol. The most thorough documentation on the DAVE protocol is found in the [Protocol Whitepaper](https://daveprotocol.com). You may additionally be able to leverage or refer to our open-source library [libdave](https://github.com/discord/libdave) to assist your implementation. The exact format of the DAVE protocol opcodes is detailed in the [Voice Gateway Opcodes section of the protocol whitepaper](https://daveprotocol.com/#voice-gateway-opcodes).
When a call is E2EE, all members of the call exchange keys via a [Messaging Layer Security](https://www.rfc-editor.org/rfc/rfc9420.html) (MLS) group. This group is used to derive per-sender ratcheted media keys (known only to the participants of the group) to encrypt/decrypt media frames sent in the call.
### Binary Websocket Messages
To reduce overhead, some of the new DAVE protocol opcodes are sent as binary instead of JSON text. See the binary column in the [Voice Opcodes Table](/developers/topics/opcodes-and-status-codes#voice) to identify these opcodes. Binary websocket messages have the following format:
| Field | Description | Size |
| --------------- | ------------------------------------------------------------------ | -------------- |
| Sequence Number | OPTIONAL (server -> client only) big-endian uint16 sequence number | 2 bytes |
| Opcode | Unsigned integer opcode value | 1 bytes |
| Payload | Binary message payload (format defined by opcode) | Variable bytes |
Sequence numbers are only sent from the server to the client, and all server-sent binary opcodes require the sequence number. See [Resuming Voice Connection](/developers/topics/voice-connections#resuming-voice-connection) for further details on how sequence numbers are used when present.
### Indicating DAVE Protocol Support
Include the highest DAVE protocol version you support in [Opcode 0 Identify](/developers/topics/opcodes-and-status-codes#voice) as `max_dave_protocol_version`. Sending version 0, or omitting the `max_dave_protocol_version` field, indicates no DAVE protocol support.
The voice gateway specifies the initial protocol version in [Opcode 4 Session Description](/developers/topics/opcodes-and-status-codes#voice) under `dave_protocol_version`. This may be any non-discontinued protocol version equal to or less than your supported protocol version.
Clients must retain backwards-compatibility of any non-discontinued DAVE protocol versions. The voice gateway selects the lowest shared protocol version for the call.
### Protocol Transitions
The voice gateway negotiates protocol version and MLS group transitions, to ensure the continuity of media being sent for the call. This can occur when the call is upgrading/downgrading to/from E2EE (in the initial transition phase), changing protocol versions, or when the MLS group is changing.
Some opcodes include a transition ID. After preparing local state necessary to perform the transition, send [Opcode 23 DAVE Protocol Transition Ready](/developers/topics/opcodes-and-status-codes#voice) to indicate to the voice gateway that you are ready to execute the transition. When all participants are ready or when a timeout has been reached, the voice gateway dispatches [Opcode 22 DAVE Protocol Execute Transition](/developers/topics/opcodes-and-status-codes#voice) to confirm execution of the transition. The transition execution is what indicates to media senders that they can begin sending media with the new protocol context (e.g. without E2EE after a downgrade, with a new protocol version after a protocol version change, or using a new key ratchet after a group participant change).
#### Downgrade
Downgrades to protocol version 0 are announced via [Opcode 21 DAVE Protocol Prepare Transition](/developers/topics/opcodes-and-status-codes#voice). This can occur during the transition phase when a client that does not support the protocol joins the call. When this transition is executed, senders should stop sending media using the protocol format.
#### Protocol Version Change & Upgrade
Protocol version transitions (including upgrades from protocol version 0) are announced via [Opcode 24 DAVE Protocol Prepare Epoch](/developers/topics/opcodes-and-status-codes#voice). In addition to the `transition_id`, this opcode includes the `epoch` for the upcoming MLS epoch.
Receiving [Opcode 24 DAVE Protocol Prepare Epoch](/developers/topics/opcodes-and-status-codes#voice) with `epoch = 1` indicates that a new MLS group is being created. Participants must:
* prepare a local MLS group with the parameters appropriate for the DAVE protocol version
* generate and send [Opcode 26 DAVE MLS Key Package](/developers/topics/opcodes-and-status-codes#voice) to deliver a new MLS key package to the voice gateway
When the `epoch` is greater than 1, the protocol version of the existing MLS group is changing.
When the transition is executed, senders must start sending media using the new protocol context (e.g. formatted for the new protocol version or using a new key ratchet).
#### MLS Group Changes
When the participants of the MLS group must change, existing participants receive an [Opcode 29 DAVE MLS Announce Commit Transition](/developers/topics/opcodes-and-status-codes#voice) whereas new members being added to the group receive [Opcode 30 DAVE MLS Welcome](/developers/topics/opcodes-and-status-codes#voice). Both opcodes include the transition ID and binary MLS Commit or MLS Welcome message.
To prepare for the protocol transition, existing group members must apply the commit to progress their local MLS group to the correct next state. [Opcode 23 DAVE Protocol Transition Ready](/developers/topics/opcodes-and-status-codes#voice) is sent when the MLS commit has been processed.
Welcomed members send [Opcode 23 DAVE Protocol Transition Ready](/developers/topics/opcodes-and-status-codes#voice) after successfully joining the group received in the MLS Welcome message.
### External Sender
The voice gateway must be an external sender of the MLS group, so that it can send external MLS proposals to add and remove call participants when appropriate (i.e. proposing the addition of new members when they connect and the removal of previous members when they disconnect).
DAVE protocol participants only process proposals which arrive from the external sender, and not from any other group members. The external sender only sends Add or Remove proposals.
The voice gateway uses [Opcode 25 DAVE MLS External Sender Package](/developers/topics/opcodes-and-status-codes#voice) to provide the external sender public key and credential to MLS group participants. This message may be sent immediately on voice gateway connection or at a later time when the call is upgrading to use the DAVE protocol.
Group creators must include the external sender they receive from the voice gateway in their MLS group extensions when creating the group. Welcomed group members ensure that the expected external sender extension is present in the group they are about to join.
### Joining the MLS Group
Except for the initial creation of the first group for the call, joining the MLS group always occurs after receiving [Opcode 30 DAVE MLS Welcome](/developers/topics/opcodes-and-status-codes#voice).
#### Key Packages
To be proposed to be added to the MLS group, pending members must send an MLS key package via [Opcode 26 DAVE MLS Key Package](/developers/topics/opcodes-and-status-codes#voice). Key packages are only used one time, and a new key package must be generated each time pending member is waiting to be added or re-added to the group.
##### Identity Public Key
MLS participants use an asymmetric keypair for MLS message signatures and authentication. The public key of this keypair is included in the key package and MLS tree. It is known to other participants in the call and is leveraged for out-of-band identity verification.
You can choose to generate a new ephemeral keypair for every protocol call or use the same persistent keypair at all times.
#### Initial Group
When there is not yet an MLS group (e.g. a transport-only encrypted call is upgrading or two members have just joined a new call) all pending group members create a local group using the MLS parameters defined by the DAVE protocol version and including the voice gateway external sender received via [Opcode 25 DAVE MLS External Sender Package](/developers/topics/opcodes-and-status-codes#voice). Every pending member of the group has the chance to produce the initial commit that creates the MLS group with `epoch = 1`.
Pending group members receive add proposals for every other pending group member from the voice gateway. If an additional pending member joins while there is not yet an MLS group, they receive all in-flight proposal messages.
Proposal and commit handling follows the same process whether or not there is an established group. See [Proposals and Commits](/developers/topics/voice-connections#proposals-and-commits)
#### Welcome
Pending group members receive a welcome message from another group member which adds them to the MLS group. This is dispatched from the voice gateway via [Opcode 30 DAVE MLS Welcome](/developers/topics/opcodes-and-status-codes#voice).
#### Invalid Group
If the group received in an [Opcode 30 DAVE MLS Welcome](/developers/topics/opcodes-and-status-codes#voice) or [Opcode 29 DAVE MLS Announce Commit Transition](/developers/topics/opcodes-and-status-codes#voice) is unprocessable, the member receiving the unprocessable message sends [Opcode 31 DAVE MLS Invalid Commit Welcome](/developers/topics/opcodes-and-status-codes#voice) to the voice gateway. Additionally, the local group state is reset and a new key package is generated and sent to the voice gateway via [Opcode 26 DAVE MLS Key Package](/developers/topics/opcodes-and-status-codes#voice).
This causes the voice gateway to propose the removal and re-addition of the requesting member.
### Proposals and Commits
The voice gateway dispatches proposals which must be appended or revoked via [Opcode 27 DAVE MLS Proposals](/developers/topics/opcodes-and-status-codes#voice). All members of the established or pending MLS group must append or revoke the proposals they receive, and then produce an MLS commit message and optionally an MLS welcome message (when committing add proposals which add new members) which they send to the voice gateway via [Opcode 28 DAVE MLS Commit Welcome](/developers/topics/opcodes-and-status-codes#voice).
In each epoch, the voice gateway dispatches the "winning" commit via [Opcode 29 DAVE MLS Announce Commit Transition](/developers/topics/opcodes-and-status-codes#voice) and optionally the associated welcome messages via [Opcode 30 DAVE MLS Welcome](/developers/topics/opcodes-and-status-codes#voice). The voice gateway broadcasts the first valid commit and welcome(s) it sees in the given epoch, and drops any commits later received for the out-of-date epoch. All dispatched unrevoked proposals in the epoch must be included in the commit for it to be valid. All members added in the epoch must be welcomed for the welcome to be valid.
### Audio Frame E2EE
While transport-encryption operates at the packet level, E2EE enabled by the DAVE protocol operates at the frame level.
When any DAVE protocol is enabled for a call, the full contents of OPUS frames sent and received by call participants are end-to-end encrypted.
#### ULEB128 Encoding
Some fields in the protocol frame payload use [ULEB128 encoding](https://en.wikipedia.org/wiki/LEB128#Unsigned_LEB128). This is a variable-length code compression to represent arbitrarily large unsigned integers in a small number of bytes.
#### Payload Format
| Field | Description | Size |
| -------------------------- | ---------------------------------------------------------- | -------------- |
| E2EE OPUS Frame | Ciphertext for E2EE OPUS frame | Variable bytes |
| AES-GCM Auth. Tag | Truncated AES128-GCM AEAD Authentication Tag | 8 bytes |
| ULEB128 Nonce | ULEB128 synchronization nonce | Variable bytes |
| ULEB128 Unencrypted Ranges | ULEB128 offset/length pairs of E2EE frame unencrypted data | Variable bytes |
| Supplemental Data Size | Unsigned integer bytes size of supplemental data | 1 byte |
| Magic Marker | 0xFAFA marker to assist with protocol frame identification | 2 bytes |
The E2EE OPUS frame is the full ciphertext resulting from the AES128-GCM AEAD encryption described below. The authentication tag is an 8-byte truncated version of the authentication tag resulting from the AEAD encryption.
The ULEB128 nonce is a variable length representation of the nonce used for encryption/decryption.
The ULEB128 unencrypted ranges field is empty (0 bytes) for OPUS frames, because the full contents of the frame are encrypted.
The supplemental data size is the sum of bytes required for:
* 8-byte authentication tag
* Variable length ULEB128 nonce
* Variable length ULEB128 unencrypted ranges
* 1 byte supplemental data size
* 2 byte magic marker
The magic marker is a constant 2-byte value 0xFAFA. This is used by media receivers to detect protocol frames as well as by the SFU to avoid sending protocol frames to non-protocol-supporting receivers during transition periods.
#### Frame Encryption
Media frames are encrypted for E2EE using AES128-GCM.
##### Sender Key Derivation
Each media sender has a ratcheted per-sender key. There is a new per-sender ratchet created in each MLS group epoch. The initial secret for each sender's ratchet is an exported 16-byte secret from the MLS group. Keys are retrieved from the ratchet via a generation counter derived from the most-significant byte of the 4-byte nonce.
For very long lived epochs, the nonce wrap-around must be handled so the generation does not also wrap back around to 0.
See the [Sender Key Derivation section of the Protocol Whitepaper](https://daveprotocol.com/#sender-key-derivation) for the detailed process.
##### Nonce
The nonce passed to the AES128-GCM encryption and decryption functions is a full 12-byte nonce, but the protocol only uses at most 4-bytes. The 12-byte nonce can be expanded from a 4-byte truncated nonce by setting the 8 most significant bytes of the nonce to zero, with the 4 least significant bytes carrying the value of the truncated nonce.
The generation used for the sender's key ratchet is retrieved from the most-significant byte of the 4-byte nonce (i.e., the 4th least significant byte of the full 12-byte nonce).
##### Authentication Tag
The authentication tag resulting from the AES128-GCM encryption is truncated to 8 bytes. Some implementations may provide the desired tag length as a parameter whereas some may always return the full 12-byte tag from which the 4 least significant bytes should be removed.
##### AEAD Additional Data
OPUS frames are fully encrypted and no additional data is passed to the AEAD.
## Transport Encryption and Sending Voice
Transport encryption between the client and the selective forwarding unit (SFU) is still used even in E2EE calls.
Voice data sent to discord should be encoded with [Opus](https://www.opus-codec.org/), using two channels (stereo) and a sample rate of 48kHz. Voice Data is sent using a [RTP Header](https://www.rfcreader.com/#rfc3550_line548), followed by encrypted Opus audio data. Voice encryption uses the key passed in [Opcode 4 Session Description](/developers/topics/opcodes-and-status-codes#voice) and the nonce formed with the 12 byte header appended with 12 null bytes to achieve the 24 required by xsalsa20\_poly1305. Discord encrypts with the [libsodium](https://download.libsodium.org/doc/) encryption library.
### Transport Encryption Modes
| Mode | Key | Nonce | Status |
| ---------------------------------- | ---------------------------------- | ----------------------------------------------------- | --------------------- |
| AEAD AES256-GCM (RTP Size) | aead\_aes256\_gcm\_rtpsize | 32-bit incremental integer value, appended to payload | Available (Preferred) |
| AEAD XChaCha20 Poly1305 (RTP Size) | aead\_xchacha20\_poly1305\_rtpsize | 32-bit incremental integer value, appended to payload | Available (Required) |
| XSalsa20 Poly1305 Lite (RTP Size) | xsalsa20\_poly1305\_lite\_rtpsize | 32-bit incremental integer value, appended to payload | Deprecated |
| AEAD AES256-GCM | aead\_aes256\_gcm | 32-bit incremental integer value, appended to payload | Deprecated |
| XSalsa20 Poly1305 | xsalsa20\_poly1305 | Copy of RTP header | Deprecated |
| XSalsa20 Poly1305 Suffix | xsalsa20\_poly1305\_suffix | 24 random bytes | Deprecated |
| XSalsa20 Poly1305 Lite | xsalsa20\_poly1305\_lite | 32-bit incremental integer value, appended to payload | Deprecated |
The deprecated encryption modes above will be discontinued as of November 18th, 2024. As of this date the voice gateway will not allow you to connect with one of the deprecated encryption modes.
The nonce has to be stripped from the payload before encrypting and before decrypting the audio data
The RTP size variants determine the unencrypted size of the RTP header in [the same way as SRTP](https://tools.ietf.org/html/rfc3711#section-3.1), which considers CSRCs and (optionally) the extension preamble to be part of the unencrypted header. The deprecated variants use a fixed size unencrypted header for RTP.
The voice gateway will report what encryption modes are available in [Opcode 2 Ready](/developers/topics/opcodes-and-status-codes#voice).
Voice gateway compatible modes will always include `aead_xchacha20_poly1305_rtpsize` but may not include `aead_aes256_gcm_rtpsize` depending on the underlying hardware. You must support `aead_xchacha20_poly1305_rtpsize`. You should prefer to use `aead_aes256_gcm_rtpsize` when it is available.
Include your selected mode in [Opcode 1 Select Protocol](/developers/topics/opcodes-and-status-codes#voice).
Finally, the voice server will respond with a [Opcode 4 Session Description](/developers/topics/opcodes-and-status-codes#voice) that includes the `mode` and `secret_key`, a 32 byte array used for [transport encryption and sending](/developers/topics/voice-connections#transport-encryption-and-sending-voice) voice data:
###### Example Session Description Payload
```json theme={"system"}
{
"op": 4,
"d": {
"mode": "aead_aes256_gcm_rtpsize",
"secret_key": [ ...251, 100, 11...],
"dave_protocol_version": 1
}
}
```
We can now start encrypting and sending voice data over the previously established UDP connection.
###### Voice Packet Structure
| Field | Type | Size |
| --------------- | ----------------------------- | ------- |
| Version + Flags | Single byte value of `0x80` | 1 byte |
| Payload Type | Single byte value of `0x78` | 1 byte |
| Sequence | Unsigned short (big endian) | 2 bytes |
| Timestamp | Unsigned integer (big endian) | 4 bytes |
| SSRC | Unsigned integer (big endian) | 4 bytes |
| Encrypted audio | Binary data | n bytes |
## Speaking
Speaking updates are used to update the **speaking modes** used by the client. This speaking mode is set using a [Opcode 5 Speaking](/developers/topics/opcodes-and-status-codes#voice) payload. The client must send this at least once before sending audio to update the SSRC and set the initial speaking mode.
The following flags can be used as a bitwise mask. For example `5` would be priority and voice. The speaking mode should not be 0 to allow sending audio.
| Flag | Meaning | Value |
| ---------- | -------------------------------------------------------------- | -------- |
| Microphone | Normal transmission of voice audio | `1 << 0` |
| Soundshare | Transmission of context audio for video, no speaking indicator | `1 << 1` |
| Priority | Priority speaker, lowering audio of other speakers | `1 << 2` |
###### Example Speaking Payload
```json theme={"system"}
{
"op": 5,
"d": {
"speaking": 5,
"delay": 0,
"ssrc": 1
}
}
```
You must send at least one [Opcode 5 Speaking](/developers/topics/opcodes-and-status-codes#voice) payload before sending voice data, or you will be disconnected with an invalid SSRC error.
The `delay` property should be set to `0` for bots that use the voice gateway.
### Voice Data Interpolation
When there's a break in the sent data, the packet transmission shouldn't simply stop. Instead, send five frames of silence (`0xF8, 0xFF, 0xFE`) before stopping to avoid unintended Opus interpolation with subsequent transmissions.
## Resuming Voice Connection
When your client detects that its connection has been severed, it should open a new WebSocket connection. Once the new connection has been opened, your client should send an [Opcode 7 Resume](/developers/topics/opcodes-and-status-codes#voice) payload:
###### Example Resume Connection Payload Since V8
```json theme={"system"}
{
"op": 7,
"d": {
"server_id": "41771983423143937",
"session_id": "my_session_id",
"token": "my_token",
"seq_ack": 10
}
}
```
###### Example Resume Connection Payload Before V8
```json theme={"system"}
{
"op": 7,
"d": {
"server_id": "41771983423143937",
"session_id": "my_session_id",
"token": "my_token"
}
}
```
If successful, the Voice server will respond with an [Opcode 9 Resumed](/developers/topics/opcodes-and-status-codes#voice) to signal that your client is now resumed:
###### Example Resumed Payload
```json theme={"system"}
{
"op": 9,
"d": null
}
```
If the resume is unsuccessful—for example, due to an invalid session—the WebSocket connection will close with the appropriate [close event code](/developers/topics/opcodes-and-status-codes#voice-voice-close-event-codes). You should then follow the [Connecting](/developers/topics/voice-connections#connecting-to-voice) flow to reconnect.
### Buffered Resume
Since voice gateway version 8, the gateway can resend buffered messages that have been lost upon resume. To support this, the gateway includes a sequence number with all messages that may need to be re-sent.
###### Example Message With Sequence Number
```json theme={"system"}
{
"op": 5,
"d": {
"speaking": 0,
"delay": 0,
"ssrc": 110
},
"seq": 10
}
```
A client using voice gateway version 8 must include the last sequence number they received under the data `d` key as `seq_ack` in both the [Opcode 3 Heartbeat](/developers/topics/opcodes-and-status-codes#voice) and [Opcode 7 Resume](/developers/topics/opcodes-and-status-codes#voice) payloads.
If no sequence numbered messages have been received, `seq_ack` can be omitted or included with a value of -1.
The gateway server uses a fixed bit length sequence number and handles wrapping the sequence number around. Since voice gateway messages will always arrive in order, a client only needs to retain the last sequence number they have seen.
If the session is successfully resumed the voice gateway will respond with an [Opcode 9 Resumed](/developers/topics/opcodes-and-status-codes#voice) and will re-send any messages that the client did not receive.
The resume may be unsuccessful if the voice gateway buffer for the session no longer contains a message that has been missed. In this case the session will be closed and you should then follow the [Connecting](/developers/topics/voice-connections#connecting-to-voice) flow to reconnect.
## IP Discovery
Generally routers on the Internet mask or obfuscate UDP ports through a process called NAT. Most users who implement voice will want to utilize IP discovery to find their external IP and port which will then be used for receiving voice communications. To retrieve your external IP and port, send the following UDP packet to your voice port (all numeric are big endian):
| Field | Description | Size |
| ------- | -------------------------------------------------------------- | -------- |
| Type | Values 0x1 and 0x2 indicate request and response, respectively | 2 bytes |
| Length | Message length excluding Type and Length fields (value 70) | 2 bytes |
| SSRC | Unsigned integer | 4 bytes |
| Address | Null-terminated string in response | 64 bytes |
| Port | Unsigned short | 2 bytes |
# Configuring App Metadata for Linked Roles
Source: https://docs.discord.com/developers/tutorials/configuring-app-metadata-for-linked-roles
Tutorial for setting up role connection metadata to link external app data with Discord roles.
Linked roles are a type of role in Discord that requires a user to connect to 3rd-party services and meet defined criteria. A role's criteria could just include the user connecting to that service, but it's often more narrow—like having a verified account, having certain stats, or having more than a certain number of followers.
Apps can define their own [role connection metadata](/developers/resources/application-role-connection-metadata), which admins can use to configure linked roles in servers where that app is installed. Apps must also set up an [OAuth2 flow](/developers/topics/oauth2) to allow users to authenticate and grant the required `role_connections.write` scope.
This tutorial walks through building a Discord app in JavaScript with linked roles support.
All of the sample code used in this tutorial can be found in the [`linked-roles-sample` GitHub repo](https://github.com/discord/linked-roles-sample)
***
## Creating an app
The first thing we’ll do is create an app through the [developer dashboard](https://discord.com/developers/applications). If you already have an app created, you can jump right to the [Running your app](/developers/tutorials/configuring-app-metadata-for-linked-roles#running-your-app) section.
Basic steps to create an app are outlined below, but a more detailed walkthrough is in the [Getting Started guide](/developers/quick-start/getting-started).
* Navigate to the [developer dashboard](https://discord.com/developers/applications)
* Click **New Application** in the upper right corner, then select a name and create your app
* Click on the [**Bot** tab](https://discord.com/developers/applications/select/bot) on the left sidebar. On that page, click **Reset Token** and store the token somewhere safe (like in a password manager)
Bot tokens are used to authorize API requests and carry your bot's permissions, making them highly sensitive. Never share your token or check it into any kind of version control.
### Adding scopes
Apps need approval from installing users to perform actions inside of Discord. So before installing your app, let's add some scopes to request during installation.
* Click on [OAuth2](https://discord.com/developers/applications/select/oauth2/url-generator) in the left sidebar, then `URL generator`
* Check the `bot` scope
* After the scope is selected, you should see a **Generated URL** which can be used to install your app
See a list of all [OAuth2 scopes](/developers/topics/oauth2#shared-resources-oauth2-scopes), or read more on [user permissions](/developers/topics/permissions) in the documentation.
### Installing your app
Copy the **Generated URL** from above, and paste it into your browser. You’ll be guided through the installation flow, where you should make sure you’re installing the app on a server where you can develop and test.
After installing your app, you can head over to your server and see that it has joined ✨
## Running your app
All of the code used in the example app can be found in the [GitHub repository](https://github.com/discord/linked-roles-sample).
### Remix the project
This guide uses Glitch, which allows you to quickly clone and develop an app from within your browser. There are also instructions on developing locally using ngrok in the README if you'd prefer.
While Glitch is great for development and testing, [it has technical limitations](https://help.glitch.com/kb/article/17-technical-restrictions/) so other hosting providers should be considered for production apps.
To start, [remix (or clone) the Glitch project 🎏](https://glitch.com/edit/#!/remix/linked-role-discord-bot)
When you remix the project, you'll see a new Glitch project with a unique name similar to this:
#### Project structure
All of the files for the project are on the left-hand side. Here's a quick glimpse at the structure:
```
├── assets -> images used in this tutorial
├── src
├──├── config.js -> Parsing of local configuration
├──├── discord.js -> Discord specific auth & API wrapper
├──├── register.js -> Tool to register the metadata schema
├──├── server.js -> Main entry point for the application
├──├── storage.js -> Provider for storing OAuth2 tokens
├── .env -> your credentials and IDs
├── .gitignore
├── package.json
└── README.md
```
### Configure your app
There's already some code in your `server.js` file, but you’ll need your app’s token and ID to make requests. All of your credentials can be stored directly in the `.env` file.
It bears repeating that you should never check any credentials or secrets into source control. The getting started project's `.gitignore` comes pre-loaded with `.env` to prevent it.
First, copy your bot user’s token from earlier and paste it in the `DISCORD_TOKEN` variable in your `.env` file.
Next, navigate to your [app settings in the developer portal](https://discord.com/developers/applications), and navigate to [OAuth2 -> General](https://discord.com/developers/applications/select/oauth2). Copy the Client ID and Client Secret for your application, and paste the values as `DISCORD_CLIENT_ID` and `DISCORD_CLIENT_SECRET` in your `.env`.
Now, we need to set the Redirect URL that will be used for our OAuth2 flow. Go back to Glitch, and click the `Share` button for your project. Copy the public live URL for your app:
Go back to the [OAuth2 -> General](https://discord.com/developers/applications/select/oauth2) tab in the Discord developer portal, and add a new redirect for your app using the Glitch URL and the `/discord-oauth-callback` route. Copy this URL, then paste it as `DISCORD_REDIRECT_URI` in your `.env`.
Go to the [General Information tab](https://discord.com/developers/applications/select/information) in the developer portal, and scroll down to the `Linked Roles Verification Url` field. Paste the base URL to your Glitch app, add the `/linked-role` route, then save.
For the Glitch project used in the screenshots, the verification URL would be `https://adjoining-crawling-yamamomo.glitch.me/linked-role`
Finally, to generate a unique cookie secret, go back to Glitch, and click on the `Terminal` tab. Run the following commands:
```
$ node
crypto.randomUUID()
```
Copy the randomly generated UUID, and paste it into your `.env` as `COOKIE_SECRET`. Your `.env` should look something like this:
```
DISCORD_CLIENT_ID:
DISCORD_CLIENT_SECRET:
DISCORD_TOKEN:
DISCORD_REDIRECT_URI: https://.glitch.me/discord-oauth-callback
COOKIE_SECRET:
```
## Registering your metadata schema
As a one-time step, you must tell Discord which metadata fields you are going to allow admins to use for linked roles associated with your app.
To configure connection metadata for your app, you'll call the [PUT /users/@me/applications/\/role-connection](/developers/resources/application-role-connection-metadata#update-application-role-connection-metadata-records) method with [application connection role metadata](/developers/resources/application-role-connection-metadata#application-role-connection-metadata-object). In the sample app, this is handled in [`src/register.js`](https://github.com/discord/linked-roles-sample/blob/main/src/register.js), and can be run via the command line.
Go back to Glitch, click the **terminal** tab, and run the following command:
```
$ node src/register.js
```
## Trying it out
Now that you've built your app, let's give it a try both from the server owner and the user's perspective.
### Creating the linked role
To try out the app, we'll create a linked role in a server where you have admin permissions. Open up the **Server Settings**, select **Roles**, and click on `Create Role`.
Give the role a name, save it, then click on `Links`. Click the `Add requirement` button, and you should see your bot in the list of available Apps. Click on it, and you will see a setup screen where you can configure specific criteria for your role.
### Acquiring the role
To acquire your newly created role, click the server name in the upper left corner of the screen, and select `Linked Roles`. Click on your role, and it will present the opportunity to connect your account.
When you connect your account, one of the scopes requested in the OAuth flow is `role_connections.write`, which is required for an app to update a user's role connection information.
Click on the linked role criteria. This should lead to the Discord OAuth2 consent screen. Click `Authorize`, and then return to Discord.
After returning to Discord, you should see your account granted the linked role.
Finally, create a new private channel, and add the new linked role.
## Tips & Tricks
### Token storage
This app largely relies on Discord's [OAuth2](/developers/topics/oauth2) implementation to obtain access tokens. This model of user based authentication relies on storing refresh tokens, and using them to acquire access tokens. The example code in [`src/storage.js`](https://github.com/discord/linked-roles-sample/blob/main/src/storage.js) uses in-memory storage to manage these tokens, but for any production deployment a database with persistent storage should be used.
### Advanced examples
For a more complex example using the Fitbit API, see [https://github.com/JustinBeckwith/fitbit-discord-bot/](https://github.com/JustinBeckwith/fitbit-discord-bot/).
# Developing A User-Installable App
Source: https://docs.discord.com/developers/tutorials/developing-a-user-installable-app
Tutorial for creating Discord apps that can be installed to user accounts.
Discord apps can be installed to servers, users, or both. This guide will walk you through building a basic game integration app that is installable to both Discord users and servers.
While the tutorial will focus on supporting different [installation contexts](/developers/resources/application#installation-context), we'll be building a basic game integration along the way with a wiki lookup with user-specific bookmarking and a server leaderboard. The app has four commands (`/link`, `/profile`, `/leaderboard`, and `/wiki`) that can be run in different installation and interaction contexts (which are concepts we'll dig into later in the tutorial).
* **[GitHub repository](https://github.com/discord/user-install-example)** where the code from this guide lives.
* **[discord-interactions](https://github.com/discord/discord-interactions-js)**, a library that provides types and helper functions for Discord apps.
* **[Express](https://expressjs.com/)**, a popular JavaScript web framework we'll use to create a server where Discord can send requests.
* **[ngrok](https://ngrok.com/)**, a tool that lets us tunnel our local server to a public URL where Discord can send requests.
***
## Step 0: Project Setup
Before we dig in, you'll need the project code from the [sample app repository](https://github.com/discord/user-install-example).
```
├── .env.sample -> sample .env file
├── app.js -> main entrypoint for the app
├── commands.js -> slash command payloads + helpers
├── game.js -> logic specific to the fake game
├── utils.js -> utility functions and enums
├── package.json
├── README.md
└── .gitignore
```
We'll be developing our app locally with a little help from [ngrok](https://ngrok.com/), but you can use your preferred development environment.
If you don't have [NodeJS](https://nodejs.org/en/download/) installed, install that first.
Now, clone the project code to your machine using the command line:
```
git clone https://github.com/discord/user-install-example.git
```
Then navigate to the directory and install the project's dependencies:
```
# navigate to directory
cd user-install-example
# install dependencies
npm install
```
With that out of the way, open your new project in the code editor of your choice, then we'll move ahead to setting up your Discord app.
## Step 1: Creating an App
First, you'll need to create an app in the developer portal if you don't have one already:
Create App
Enter a name for your app, then press **Create**.
After you create your app, you'll land on the **General Information** page of the app's settings where you can update basic information about your app like its description and icon.
### Fetching app credentials
While we're in your app's settings, we'll want to get a few sensitive values for your app, like its token and ID.
Your token is used to authorize API requests and carry your app's permissions, so they are *highly* sensitive. Make sure to never share your token or check it into any kind of version control.
Back in your project folder, rename the `.env.sample` file to `.env`. `.env` is where we'll store all of your app's credentials.
We'll need three values from your app's settings for your `.env` file:
* On the [**General Information** page](https://discord.com/developers/applications/select/information), copy the value for **Application ID**. In `.env`, replace `` with the ID you copied.
* Back on the [**General Information** page](https://discord.com/developers/applications/select/information), copy the value for **Public Key**, which is used to ensure HTTP requests are coming from Discord. In `.env`, replace `` with the value you copied.
* On the [**Bot** page](https://discord.com/developers/applications/select/bot) under **Token**, click "Reset Token" to generate a new bot token. In `.env`, replace `` with your new token.
Now that you have the credentials you need, we'll configure your app to support different installation contexts.
### Add Guild Members intent
The sample app fetches members in the server when constructing a fake game leaderboard. Getting server members requires a special permission called a [privileged intent](/developers/events/gateway#privileged-intents), so we'll add that to our app.
Go to the **Bot** page and find the **Privileged Gateway Intents** section. Toggle "Server Member Intent" to be active.
### Choosing Supported Installation Contexts
An app's **[installation context](/developers/resources/application#installation-context)** defines how it's installed: to a server, to a user, or both.
We're going to configure our app to support both installation contexts, and while that's a good default for most apps, some apps may only make sense in one context or the other.
In your app's settings, go to the [**Installation** page](https://discord.com/developers/applications/select/installation) from the sidebar. Under **Installation Contexts**, check both **User Install** and **Guild Install**, then press **Save Changes**.
### Configuring Default Install Settings
The default install settings of your app determines the default [scopes](/developers/topics/oauth2#shared-resources-oauth2-scopes) and [bot user permissions](/developers/topics/permissions) for each supported installation context. At the moment, apps installed to a user context only support the `applications.commands` scope (which allows your app to install [commands](/developers/interactions/application-commands)) in the default install settings.
###### Update Install Link
Before adding default install settings, we need to select Discord Provided Link for the app's [install link](/developers/resources/application#install-links). Under the **Install Link** section, select `Discord Provided Link` from the dropdown if it isn't already selected (it should be by default). Once its selected, the **Default Install Settings** will appear.
###### Adding Default Install Settings
Under the **Default Install Settings** section:
* For **User Install**, add the `applications.commands` scope
* For **Guild Install**, add the `applications.commands` scope and `bot` scope. When you select `bot`, a new **Permissions** menu will appear to select the bot user's permissions. Select any permissions that you may want for your app—for now, I'll just select `Send Messages`.
Permissions for a bot user are very similar to permissions for other Discord users. Details about permissions, and a list of available permissions is on the [Permissions](/developers/topics/permissions#permissions-bitwise-permission-flags) page.
After you've selected the scopes and permissions for your app, click **Save Changes**.
### Installing your app
Finally, we'll install your new app to both a test server and your user account so that we can test in both installation contexts.
###### Install to server
To install your app to your test server, copy the default Install Link for your app from the **Installation** page. Paste the link in your browser and hit enter, then select "Add to server" in the installation prompt.
Select your test server, and follow the installation prompt. Once your app is added to your test server, you should see it appear in the member list.
###### Install to user account
Next, install your app to your user account. Paste the same Install Link in your browser and hit enter. This time, select "Add to my apps" in the installation prompt.
Follow the installation prompt to install your app to your user account. Once it's installed you can open a DM with it.
## Step 2: Setting Up Commands
Next, we'll register the [application commands](/developers/interactions/application-commands) for our app. But before touching code, it's important to understand the concept of command contexts:
Commands have two context fields that can be set when creating or updating a command which let you limit the supported install methods and surfaces in Discord for that command:
* **`integration_types`** lets you control which **[installation contexts](/developers/interactions/application-commands#installation-context)** a command is supported (user, guild, or both). For example, the `/link` and `/profile` commands we'll be creating are only available when the app is installed to a user.
* **`contexts`** lets you set the **[interaction contexts](/developers/interactions/application-commands#interaction-contexts)**, or the surfaces in Discord, where a command can be used (in a guild channel, in your bot user's DM, and within other DMs or GDMs). For example, the `/leaderboard` command we'll be creating is only available when the command is run from a guild channel.
More information and details about command contexts are in the [contexts](/developers/interactions/application-commands#contexts) documentation, but for now we'll get a better understanding of contexts by using them in our sample app.
### Commands in the sample project
We'll be setting up four commands for our sample app that all have *slightly* different contexts, which are included in the table below:
| Name | Description | Installation Contexts (`integration_types`) | Interaction Contexts (`contexts`) |
| -------------- | ------------------------------------------------------ | ------------------------------------------- | ------------------------------------ |
| `/leaderboard` | View game leaderboard for the current server | `GUILD_INSTALL` | `GUILD` |
| `/wiki` | Find information about game items and characters | `GUILD_INSTALL`, `USER_INSTALL` | `GUILD`, `BOT_DM`, `PRIVATE_CHANNEL` |
| `/profile` | Get information about your game inventory and progress | `USER_INSTALL` | `GUILD`, `BOT_DM`, `PRIVATE_CHANNEL` |
| `/link` | Link your game account to Discord | `USER_INSTALL` | `BOT_DM` |
The supported installation contexts for a command affects which interaction contexts you can set. Specifically, the `PRIVATE_CHANNEL` interaction context can only be included in `contexts` if `USER_INSTALL` is included in `integration_types` for the command. Read details in the [documentation](/developers/interactions/application-commands#contexts).
The payloads for our app's commands are in `commands.js` in the project folder in case you want to change any values or see what the command's context fields (`integration_types` and `contexts`) look like for each of the commands in the table above.
### Registering the commands
Now let's register your app's commands so you can see them in Discord. In your project folder run:
```
npm run register
```
The register command will call the [Create Global Application Command](/developers/interactions/application-commands#create-global-application-command) endpoint for each of the command payloads in `commands.js`.
After your new commands have been created, you can go into Discord and look for the commands in the surfaces where we made them available:
* In **channels within the guild you installed your app**, you should see `/leaderboard`, `/wiki`, and `/profile`
* In **channels within any of your guilds**, you should see `/wiki` and `/profile`
* In **your app's DM**, you should see `/wiki`, `/profile`, and `link`
* And finally, **in DMs or GDMs with other users**, you should see `/wiki` and `/profile`
However, if you try to run any of the commands, you'll get an error :(
...let's fix that.
## Step 3: Handling Interactivity
To receive and handle interactive requests, we'll set up an **Interactions Endpoint URL**, which is a public URL where Discord sends your app's interactions.
### Set up a public endpoint
To set up a public endpoint we'll start our app, which runs an [Express](https://expressjs.com/) server, then use [ngrok](https://ngrok.com/) to expose our server publicly.
First, go to your project's folder and run the following to start your app:
```
npm run start
```
There should be some output indicating your app is running on port 3000. Behind the scenes, our app is ready to handle interactions from Discord, which includes verifying security request headers and responding to `PING` requests. We're skipping over a lot of the details in this tutorial, but details about preparing apps for interactions is in the [Interactions Overview](/developers/interactions/overview#preparing-for-interactions) documentation.
By default, the server will listen to requests sent to port 3000, but if you want to change the port, you can specify a `PORT` variable in your `.env` file.
Next, we'll start our ngrok tunnel. If you don't have ngrok installed locally, you can install it by following the instructions on the [ngrok download page](https://ngrok.com/download).
After ngrok is installed locally, open a new terminal and create a public endpoint that will forward requests to your Express server:
```
ngrok http 3000
```
The output will include a **Forwarding** URL, which is the publicly-accessible URL we'll use for our Interactions Endpoint URL in the next step.
### Configuring an interaction endpoint URL
Let's configure our app's **Interaction Endpoint URL**.
Go to your [app's settings](https://discord.com/developers/applications) and on the [**General Information** page](https://discord.com/developers/applications/select/information) under **Interaction Endpoint URL**, paste your new ngrok URL and append `/interactions` (it'll be something like `https://84c5df474.ngrok-free.dev/interactions`).
Click **Save Changes** and if all is well, your Interactions Endpoint URL should be verified by Discord.
If you have troubles verifying your endpoint, make sure both ngrok and your app is running on the same port, and that you've copied the ngrok URL correctly
### Understanding metadata for interactions
Now that our Interactions Endpoint URL is set up, we should now be able to run our app's commands. Go to your app's DM and run `/profile`, and your app should respond with a sample game profile.
Back on the command line, our app is logging incoming requests from Discord, so you can see what the request body for your command invocation looked like.
The payload below is condensed to be more readable, but your interaction request body should look something like this:
```json theme={"system"}
{
"app_permissions": "442368",
"application_id": "234248956100616262",
"authorizing_integration_owners": { "1": "1090372582781497424" },
"channel": {
// Partial channel object corresponding to channel_id
},
"channel_id": "1234563982236504123",
"context": 1,
"data": { "id": "1234358421659193405", "name": "link", "type": 1 },
"entitlements": [],
"id": "1234968734674853908",
"locale": "en-US",
"token": "a really long interactions token that your app can use to respond to the interactions",
"type": 2,
"user": {
// Partial user object
},
"version": 1
}
```
To see which command was run, you can look at the [`data` object](/developers/interactions/receiving-and-responding#interaction-object-interaction-data).
However, for this tutorial, we're going to focus more on the metadata related to installation and interaction contexts. There are a few metadata fields you'll want to pay attention to when building an app that can be installed to multiple interaction contexts—
###### `context`
`context` tells you which [interaction context](/developers/interactions/application-commands#interaction-contexts) the command was invoked from. Since I triggered the command from my app's DM the `context` is `1` (or `BOT_DM`).
With interaction context, something to keep in mind in `BOT_DM` is only the *DM with your bot user*. If you run the same command in a DM with your bestie, or in a group DM, the interaction context will be `PRIVATE_CHANNEL` (`2`).
###### `authorizing_integration_owners`
`authorizing_integration_owners` provides data about any ID relevant to the installation context(s) associated with the interaction.
The keys in the object are the relevant installation context(s) (`GUILD_INSTALL`/`"0"` and/or `USER_INSTALL`/`"1"`). The values depend on the key, but for `USER_INSTALL` the key will always be the ID of the user that authorized your app.
`authorizing_integration_owners` is not the same as the user that triggered the interaction. Information about the user that triggered the interaction is in the `user` object.
Understanding the authorization owner can be helpful when handling interactions from message components for apps installed to a user, which is discussed more in the [message component interactions](/developers/tutorials/developing-a-user-installable-app#using-metadata-for-message-component-interactions) section. Or you can find technical details in the [Authorizing Integration Owners](/developers/interactions/receiving-and-responding#interaction-object-authorizing-integration-owners-object) documentation.
###### `app_permissions`
`app_permissions` are the bitwise set of permissions your app has in the place where the interaction was triggered. The permissions your app has will be different for DMs with your app, in servers, and G(DM)s with other users.
In the sample payload, the value is `"442368"`.
These values can be helpful when deciding how you want your app to [respond to the interaction](/developers/interactions/receiving-and-responding#responding-to-an-interaction). For example, perhaps you want your app to respond ephemerally when a specific command is invoked from a server, which the sample app does for the `/profile` command.
### Using metadata for command interactions
As mentioned above, the `/profile` command for our app will respond ephemerally, meaning only the invoking user will see the response, when invoked from a server. If it's invoked within a DM with the bot user, it'll respond with a non-ephemeral message. In the project, you can see this logic in `app.js` when handling the `/profile` command:
Below is the logic for handling the `/profile` command. When the context for the interaction is in a server, we'll make our interaction response ephemeral and add a button so the user can share their profile if they want.
```javascript theme={"system"}
// "profile" command
if (name === 'profile') {
const profile = getFakeProfile(0);
const profileEmbed = createPlayerEmbed(profile);
// Use interaction context that the interaction was triggered from
const interactionContext = req.body.context;
// Construct `data` for our interaction response. The profile embed will be included regardless of interaction context
let profilePayloadData = {
embeds: [profileEmbed],
};
// If profile isn't run in a DM with the app, we'll make the response ephemeral and add a share button
if (interactionContext !== 1) {
// Make message ephemeral
profilePayloadData['flags'] = 64;
// Add button to components
profilePayloadData['components'] = [
{
type: 1,
components: [
{
type: 2,
label: 'Share Profile',
custom_id: 'share_profile',
style: 2,
},
],
},
];
}
// Send response
return res.send({
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
data: profilePayloadData,
});
}
```
In the sample app code, we assign the value of `context` in the request body to a new `interactionContext` variable. Based on the context, we modify how we respond to the command interaction. If it was run in a guild or within a G(DM) other than the DM with the app's bot user, `flags` is set to `64` to make the response ephemeral, and a new button component is added so that the user can share their profile if they want.
### Using metadata for message component interactions
Message component interactions can be triggered by any user the component is visible to, regardless of the installation context. Since potentially any user can trigger the component, it can be helpful to use metadata to understand context about the installation.
Consider we added a new `/game` command supported in the `USER_INSTALL` installation context that a user could trigger to send a message to whatever guild or group DM they're in to ask others if they're interested in joining a game match:
When someone clicks on the button, our app would care about two users when handling the interaction:
1. User B, who clicked the "Join" button so our app can track who is interested in joining the match
2. User A, who ran the `/game` command so our app can tell them who is interested in joining the match
There are two fields additional fields to know about that can be helpful in this scenario—
###### `interaction_metadata`
Messages created in response to an interaction will include an [`interaction_metadata` object](/developers/resources/message#message-interaction-metadata-object) which includes metadata related to the interaction.
###### `authorizing_integration_owners`
`authorizing_integration_owners` was touched on above, but it's worth highlighting again since it's most helpful when handling message component interactions for user-installed apps.
For user-installed apps, it can be used to differentiate between the user that installed an app and the user that triggered an app's interaction since messages sent in response to interactions (either an interaction response or a follow-up message) can be visible to users that don't have the app installed to their account.
## Next Steps
*Yay\~!* At this point, you have an app that supports both installation contexts and understand the basics of using metadata to support different contexts. Now you can go explore the documentation for details, or play with the sample app to develop more complex features.
Explore the Interactions documentation to learn more about receiving and responding to commands and message components
GitHub repository with sample project
# Hosting a Reddit API Discord app on Cloudflare Workers
Source: https://docs.discord.com/developers/tutorials/hosting-on-cloudflare-workers
Tutorial for deploying Discord apps on Cloudflare Workers.
When building Discord apps, your app can receive common events from the client as [webhooks](/developers/resources/webhook) when users interact with your app through interactions like [application commands](/developers/interactions/application-commands) or [message components](/developers/components/reference).
Discord will send these events to a pre-configured HTTPS endpoint (called an Interactions Endpoint URL in an app's configuration) as a JSON payload with details about the event.
This tutorial walks through building a Discord app powered by [`r/aww`](https://www.reddit.com/r/aww) using JavaScript.
All of the code for this app can be found **[on GitHub](https://github.com/discord/cloudflare-sample-app)**.
### Features and technologies used
* [Discord Interactions API](/developers/interactions/receiving-and-responding) (specifically slash commands)
* [Cloudflare Workers](https://workers.cloudflare.com/) for hosting
* [Reddit API](https://www.reddit.com/dev/api/) to send messages back to the user
***
## Creating an app on Discord
To start, we'll create the app through the [Discord Developer Dashboard](https://discord.com/developers/applications):
* Visit [https://discord.com/developers/applications](https://discord.com/developers/applications)
* Click `New Application`, and choose a name
* Copy your **Public Key** and **Application ID**, and put them somewhere locally (we'll need these later)
* Now click on the [**Bot** tab](https://discord.com/developers/applications/select/bot) on the left sidebar.
* Grab the `token` for your bot, and store it somewhere safe (I like to put these tokens in a password manager like [1password](https://1password.com/) or [lastpass](https://www.lastpass.com/)).
For security reasons, you can only view your bot token once. If you misplace your token, you'll have to generate a new one.
## Adding bot permissions
Now we'll configure the bot with [permissions](/developers/topics/permissions) required to create and use slash commands, as well as send messages in channels.
* Click on the [OAuth2 tab](https://discord.com/developers/applications/select/oauth2/url-generator), and choose the `URL Generator`. Click the `bot` and `applications.commands` scopes.
* Check the boxes next to `Send Messages` and `Use Slash Commands`, then copy the `Generated URL`.
* Paste the URL into the browser and follow the OAuth flow, selecting the server where you'd like to develop and test your bot.
## Creating your Cloudflare Worker
Cloudflare Workers are a convenient way to host Discord apps due to the free tier, simple development model, and automatically managed environment (no VMs!).
When using Cloudflare Workers, your app won't be able to access non-ephemeral CDN media. For example, trying to fetch an image like `https://cdn.discordapp.com/attachments/1234/56789/my_image.png` would result in a `403` error. Cloudflare Workers are still able to access ephemeral CDN media.
* Visit the [Cloudflare Dashboard](https://dash.cloudflare.com/)
* Click on the `Workers` tab, and create a new service using the same name as your Discord bot
* Make sure to [install the Wrangler CLI](https://developers.cloudflare.com/workers/cli-wrangler/install-update/) and set it up.
### Storing secrets
The production service needs access to some of the information we saved earlier. To set those variables, run:
```
$ wrangler secret put DISCORD_TOKEN
$ wrangler secret put DISCORD_PUBLIC_KEY
$ wrangler secret put DISCORD_APPLICATION_ID
```
You'll also need the Guild ID for the server where your app is installed. This can be found in the URL when you visit any channel in that server.
For example, if my URL was `https://discord.com/channels/123456/789101112`, the Guild ID is the first number—in this case **`123456`**.
Once you know your Guild ID, set that variable as well:
```
$ wrangler secret put DISCORD_TEST_GUILD_ID
```
## Running locally
This depends on the beta version of the `wrangler` package, which better supports ESM on Cloudflare Workers.
Let's start by cloning the repository and installing dependencies. This requires at least v16 of [Node.js](https://nodejs.org/en/):
```
$ npm install
```
### Project structure
A brief look at the cloned app's project structure:
```
├── .github/workflows/ci.yaml -> GitHub Action configuration
├── src
├── ├── commands.js -> JSON payloads for commands
├── ├── reddit.js -> Interactions with the Reddit API
├── ├── register.js -> Sets up commands with the Discord API
├── ├── server.js -> Discord app logic and routing
├── test
├── ├── test.js -> Tests for app
├── wrangler.toml -> Configuration for Cloudflare Workers
├── package.json
├── README.md
├── renovate.json -> Configuration for repo automation
├── .eslintrc.json
├── .prettierignore
├── .prettierrc.json
└── .gitignore
```
### Registering commands
Before testing our app, we need to register our desired slash commands. For this app, we'll have a `/awwww` command, and a `/invite` command. The name and description for these are kept separate in `commands.js`:
```js theme={"system"}
export const AWW_COMMAND = {
name: 'awwww',
description: 'Drop some cuteness on this channel.',
};
export const INVITE_COMMAND = {
name: 'invite',
description: 'Get an invite link to add the bot to your server',
};
```
The code to register commands lives in `register.js`. Commands can be [registered globally](/developers/interactions/application-commands#create-global-application-command), making them available for all servers with the app installed, or they can be [registered on a single server](/developers/interactions/application-commands#create-guild-application-command).
In this example - we'll just focus on global commands:
```js theme={"system"}
import { AWW_COMMAND, INVITE_COMMAND } from './commands.js';
import fetch from 'node-fetch';
/**
* This file is meant to be run from the command line, and is not used by the
* application server. It's allowed to use node.js primitives, and only needs
* to be run once.
*/
const token = process.env.DISCORD_TOKEN;
const applicationId = process.env.DISCORD_APPLICATION_ID;
if (!token) {
throw new Error('The DISCORD_TOKEN environment variable is required.');
}
if (!applicationId) {
throw new Error(
'The DISCORD_APPLICATION_ID environment variable is required.'
);
}
/**
* Register all commands globally. This can take o(minutes), so wait until
* you're sure these are the commands you want.
*/
async function registerGlobalCommands() {
const url = `https://discord.com/api/v10/applications/${applicationId}/commands`;
await registerCommands(url);
}
async function registerCommands(url) {
const response = await fetch(url, {
headers: {
'Content-Type': 'application/json',
Authorization: `Bot ${token}`,
},
method: 'PUT',
body: JSON.stringify([AWW_COMMAND, INVITE_COMMAND]),
});
if (response.ok) {
console.log('Registered all commands');
} else {
console.error('Error registering commands');
const text = await response.text();
console.error(text);
}
return response;
}
await registerGlobalCommands();
```
### Running the server
This command needs to be run locally, once before getting started:
```
$ DISCORD_TOKEN=**** DISCORD_APPLICATION_ID=**** node src/register.js
```
We're finally ready to run this code locally! Let's start by running our local development server:
```
$ npm run dev
```
### Setting up ngrok
When a user types a slash command, Discord will send an HTTP request to a public endpoint. During local development this can be a little challenging, so we're going to use [a tool called `ngrok`](https://ngrok.com/) to create an HTTP tunnel.
```
$ npm run ngrok
```
This is going to bounce requests off of an external endpoint, and forward them to your machine. Copy the HTTPS link provided by the tool. It should look something like `https://8098-24-22-245-250.ngrok.io`.
Now head back to the Discord Developer Dashboard, and update the `Interactions Endpoint URL` for your app:
This is the process we'll use for local testing and development. When you've published your app to Cloudflare, you will **want to update this field to use your Cloudflare Worker URL.**
## Deployment
This repository is set up to automatically deploy to Cloudflare Workers when new changes land on the `main` branch. To deploy manually, run `npm run publish`, which uses the `wrangler publish` command under the hood.
Publishing via a GitHub Action requires obtaining an [API Token and your Account ID from Cloudflare](https://developers.cloudflare.com/workers/cli-wrangler/authentication/). These are stored [as secrets in the GitHub repository](https://docs.github.com/en/actions/security-guides/encrypted-secrets), making them available to GitHub Actions.
The following configuration in `.github/workflows/ci.yaml` demonstrates how to tie it all together:
```yaml theme={"system"}
release:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
needs: [test, lint]
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16
- run: npm install
- run: npm run publish
env:
CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
CF_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}
```
## Code deep dive
Most of the interesting code in this app lives in `src/server.js`. Cloudflare Workers require exposing a `fetch` function, which is called as the entry point for each request. This code will largely do two things for us: validate the request is valid and actually came from Discord, and hand the request over to a router to help give us a little more control over execution.
```js theme={"system"}
export default {
/**
* Every request to a worker will start in the `fetch` method.
* Verify the signature with the request, and dispatch to the router.
* @param {*} request A Fetch Request object
* @param {*} env A map of key/value pairs with env vars and secrets from the cloudflare env.
* @returns
*/
async fetch(request, env) {
if (request.method === 'POST') {
// Using the incoming headers, verify this request actually came from discord.
const signature = request.headers.get('x-signature-ed25519');
const timestamp = request.headers.get('x-signature-timestamp');
const body = await request.clone().arrayBuffer();
const isValidRequest = verifyKey(
body,
signature,
timestamp,
env.DISCORD_PUBLIC_KEY
);
if (!isValidRequest) {
console.error('Invalid Request');
return new Response('Bad request signature.', { status: 401 });
}
}
// Dispatch the request to the appropriate route
return router.handle(request, env);
},
};
```
All of the API calls from Discord in this example will be POSTed to `/`. From here, we will use the [`discord-interactions`](https://github.com/discord/discord-interactions-js) npm module to help us interpret the event, and to send results.
```js theme={"system"}
/**
* Main route for all requests sent from Discord. All incoming messages will
* include a JSON payload described here:
* /developers/docs/interactions/receiving-and-responding#interaction-object
*/
router.post('/', async (request, env) => {
const message = await request.json();
console.log(message);
if (message.type === InteractionType.PING) {
// The `PING` message is used during the initial webhook handshake, and is
// required to configure the webhook in the developer portal.
console.log('Handling Ping request');
return new JsonResponse({
type: InteractionResponseType.PONG,
});
}
if (message.type === InteractionType.APPLICATION_COMMAND) {
// Most user commands will come as `APPLICATION_COMMAND`.
switch (message.data.name.toLowerCase()) {
case AWW_COMMAND.name.toLowerCase(): {
console.log('handling cute request');
const cuteUrl = await getCuteUrl();
return new JsonResponse({
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
data: {
content: cuteUrl,
},
});
}
case INVITE_COMMAND.name.toLowerCase(): {
const applicationId = env.DISCORD_APPLICATION_ID;
const INVITE_URL = `https://discord.com/oauth2/authorize?client_id=${applicationId}&scope=applications.commands`;
return new JsonResponse({
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
data: {
content: INVITE_URL,
flags: InteractionResponseFlags.EPHEMERAL,
},
});
}
default:
console.error('Unknown Command');
return new JsonResponse({ error: 'Unknown Type' }, { status: 400 });
}
}
console.error('Unknown Type');
return new JsonResponse({ error: 'Unknown Type' }, { status: 400 });
});
```
## Next steps
In case you need to reference any of the code, you can find the repo [on GitHub](https://github.com/discord/cloudflare-sample-app)
With your app built and deployed, you can start customizing it to be your own:
* Use **[message components](/developers/components/reference)** in your app to add more interactivity (like buttons and select menus).
* Take a look at different **[public APIs](https://github.com/public-apis/public-apis)** on GitHub.
* Join the **[Discord Developers server](https://discord.gg/discord-developers)** to ask questions about the API, attend events hosted by the Discord API team, and interact with other developers.
# Using Community Invites
Source: https://docs.discord.com/developers/tutorials/using-community-invites
Automate role assignment and targeted invites using the Discord Community Invites API.
Community invites extend Discord's invite system with two API-powered features: the `role_ids` parameter for automatic role assignment, and the `target_users_file` parameter for restricting which users can accept an invite.
If you're a server admin or community manager looking to create role invites without code, see the [Community Invites guide](/developers/communities/guides/community-invites) for the Discord UI walkthrough.
This tutorial will walk you through using community invites programmatically in your bot or app. By the end, you'll know how to:
* Create invites that assign roles through the API
* Create targeted invites for specific groups of users through the API
***
## Understanding Community Invites Features
Community invites extend Discord's existing invite system with two powerful features designed for game integrations:
### Role Assignment
When using the API, the `role_ids` parameter lets you specify one or more Discord roles that will be automatically assigned when a player accepts the invite. No manual role management needed.
**Example use cases:**
* Giving roles to supporters
* Giving roles related to characters or achievements from your game
* Giving roles that have permissions to access channels
Once a user accepts an invite and receives the roles, those roles remain even after the invite expires or is deleted. You'll need to remove roles manually (via a bot or Discord's interface).
### Targeted Invites
Setting which users that are allowed to accept an invite can only be done through the Discord API. The `target_users_file` parameter accepts a CSV file containing Discord user IDs. Only users in that list can see and accept the invite.
**Example use cases:**
* Preventing invite sharing for exclusive achievements (for example, create a unique invite for a player with a 100% completion role)
* When combined with roles to give out mod/admin privileges
* Combined with roles to give special roles/channel access to paying supporters
These features work with all standard invite parameters like `max_age`, `max_uses`, and more.
***
## Role Granting Invite Example: Auto-Assigning Player Roles
In this example, we'll show you how to grant roles as part of an invite using the [channel invite API](/developers/resources/channel#create-channel-invite). We'll take a look at what a game developer could do to connect their game back to their Discord server.
### Prerequisites
Before you begin, make sure you have:
* A Discord server for testing
* Developer Mode enabled on your account (so you can copy role, channel, and user IDs)
* A Discord app with a bot token (create one in the [Developer Portal](https://discord.com/developers/applications))
* Node.js installed, or the ability to make HTTP requests from your bot or app
* Bot permissions for:
* **Create Instant Invite** (required to create invites)
* **Manage Roles** (required for automatic role assignment with `role_ids`)
### Coding the Invite
When players join your Discord server from your game, you can give them a **Player** role for access to game-specific channels. Here's how to create an invite that does this automatically:
```javascript theme={"system"}
// IMPORTANT: Never hardcode tokens or commit them to version control
// Use environment variables or a secure configuration management system
// Replace the placeholder below with your actual bot token, channel ID, and role ID
const BOT_TOKEN = 'REPLACE_WITH_YOUR_BOT_TOKEN_FROM_THE_DEV_PORTAL';
const CHANNEL_ID = '1234567890123456789';
const ROLE_ID = '9876543210987654321';
const response = await fetch(
`https://discord.com/api/v10/channels/${CHANNEL_ID}/invites`,
{
method: 'POST',
headers: {
'Authorization': `Bot ${BOT_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
role_ids: [ROLE_ID]
})
}
);
if (!response.ok) {
const error = await response.json().catch(() => ({
message: `HTTP ${response.status}`
}));
throw new Error(
`Failed to create invite: ${error.message}${error.code ? ` (code: ${error.code})` : ''}`
);
}
const invite = await response.json();
console.log(`Created invite: https://discord.gg/${invite.code}`);
```
### What's Happening
1. **`role_ids` array**: Contains the Discord role IDs to assign when the invite is accepted (we're only assigning one in this example, but you can assign any number of roles using this array)
2. **Response**: Returns an invite object with a `code` you can share
### Testing It Out
Create a **Player** role in your Discord server
Right click the role and click **"Copy Role ID"** then paste it in `ROLE_ID` in the script
Right click the channel for the invite and click **"Copy Channel ID"** then paste it in `CHANNEL_ID` in the script
Paste your bot token in `BOT_TOKEN` in the script
Run the code to generate an invite code
Share the `discord.gg/...` link with a test account
When they accept, they'll automatically get the **Player** role!
***
## Target Users Example: Creating Targeted Supporter Invites
In this example, we'll show you how to only allow specific users to accept an invite using the [channel invite API](/developers/resources/channel#create-channel-invite). We'll take a look at what a game developer or streamer could do to grant special roles and channels in their community for supporters.
### Prerequisites
Before you begin, make sure you have:
* A Discord server for testing
* Developer Mode enabled on your account (so you can copy role, channel, and user IDs)
* A Discord app with a bot token (create one in the [Developer Portal](https://discord.com/developers/applications))
* Node.js installed, or the ability to make HTTP requests from your bot or app
* Bot permissions for:
* **Create Instant Invite** (required to create invites)
* **Manage Guild** (required for targeted invites with `target_users_file`)
* **Manage Roles** (required for automatic role assignment with `role_ids`)
### Coding the Invite
If you run a subscription service for your game or community, you can create exclusive invites that only your paying supporters can accept. Those roles can grant access to channels that other users in the server can't see. Here's how to create an invite that grants a role to specific users:
```javascript theme={"system"}
// IMPORTANT: Never hardcode tokens or commit them to version control
// Use environment variables or a secure configuration management system
// Replace the placeholder below with your actual bot token, channel ID, and role ID
const BOT_TOKEN = "REPLACE_WITH_YOUR_BOT_TOKEN_FROM_THE_DEV_PORTAL";
const SUPPORTER_CHANNEL_ID = '1234567890123456789';
const SUPPORTER_ROLE_ID = '1111222233334444555';
// In production you would fetch this from your subscription database
// Replace these example user IDs with actual Discord user IDs of your test accounts
const activeSupporterIds = [
'111111111111111111',
'222222222222222222',
'333333333333333333',
];
// Create CSV content with user IDs
const csvContent = activeSupporterIds.join('\n');
const csvBlob = new Blob([csvContent], { type: 'text/csv' });
// Use FormData for multipart/form-data request
const formData = new FormData();
formData.append('target_users_file', csvBlob, 'supporters.csv');
formData.append('payload_json', JSON.stringify({
role_ids: [SUPPORTER_ROLE_ID]
}));
const response = await fetch(
`https://discord.com/api/v10/channels/${SUPPORTER_CHANNEL_ID}/invites`,
{
method: 'POST',
headers: {
'Authorization': `Bot ${BOT_TOKEN}`
},
body: formData
}
);
if (!response.ok) {
const error = await response.json().catch(() => ({
message: `HTTP ${response.status}`
}));
let errorMessage = `Failed to create invite: ${error.message || response.statusText}`;
if (error.code) {
errorMessage += ` (code: ${error.code})`;
}
if (error.errors) {
errorMessage += '\nDetailed errors:\n' + JSON.stringify(error.errors, null, 2);
}
throw new Error(errorMessage);
}
const invite = await response.json();
console.log(`Share this link with supporters: https://discord.gg/${invite.code}`);
```
### What's Happening
1. **`target_users_file`**: A CSV file containing Discord user IDs of paying supporters. Only these users can see and accept the invite.
2. **`role_ids` array**: Assigns the **Supporter** role automatically, giving them access to exclusive channels
3. **FormData**: Required when uploading files, the other parameters go in `payload_json`
When using only JSON parameters like `role_ids`, use `Content-Type: application/json`. When uploading `target_users_file`, you must use `multipart/form-data`.
### Setting Up the Supporter Role
For the best experience, configure your **Supporter** role with these permissions:
1. Create a **Supporter** role in your Discord server
2. Create a private channel (#supporter-polls or #supporter-chat)
3. Right click the channels and **"Edit Channel"** to set the channel permissions so only users with the **Supporter** role can view and message in it
* **View Channel** for the supporter exclusive channels
* **Send Messages** and **Add Reactions**
### Testing It Out
Create a **Supporter** role in your Discord server
Right click the role and click **"Copy Role ID"** then paste it in `SUPPORTER_ROLE_ID` in the script
Right click the channel for the invite and click **"Copy Channel ID"** then paste it in `SUPPORTER_CHANNEL_ID` in the script
Paste your bot token in `BOT_TOKEN` in the script
Right click each user you want to be able to accept the invite and click **"Copy User ID"** then paste that in `activeSupporterIds` in the script
Run the code to generate an invite code
Share the `discord.gg/...` link with one of the users you added to `activeSupporterIds` and one that you didn't
The users in `activeSupporterIds` will be able to accept and receive the **Supporter** role!
***
## Resources
For complete API reference details, check out the invite and channel API documentation.
Complete API reference for invite objects, endpoints, and parameters
Detailed documentation for the create invite endpoint