What we'll be building
What we'll be building

- User A initiates a new game and picks their object using the app’s /challengeslash command
- A message is sent to channel with a button inviting others to accept the challenge
- User B presses the Accept button
- User B is sent an ephemeral message where they select their object of choice
- The result of the game is posted back into the original channel for all to see
Resources used in this guide
Overview of the tools and technologies we'll use
Resources used in this guide
Overview of the tools and technologies we'll use
- GitHub repository where the code from this guide lives along with some additional feature-specific code examples.
- discord-interactions, a library that provides types and helper functions for Discord apps.
- Express, a popular JavaScript web framework we’ll use to create a server where Discord can send us requests.
- ngrok, 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.Project structure
Overview of the project structure for the sample app used in this tutorial
Project structure
Overview of the project structure for the sample app used in this tutorial
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..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, copy the value for Application ID. In .env, replace<YOUR_APP_ID>with the ID you copied.
- Back on the General Information page, copy the value for Public Key, which is used to ensure HTTP requests are coming from Discord. In .env, replace<YOUR_PUBLIC_KEY>with the value you copied.
- On the Bot page under Token, click “Reset Token” to generate a new bot token. In .env, replace<YOUR_BOT_TOKEN>with your new token.
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. On the left hand sidebar in your app’s settings, there’s a Bot page (where we fetched the token from). On this page, you can also configure settings like its privileged intents or whether it can be installed by other users.What are intents?
Introduction to standard and privileged intents
What are intents?
Introduction to standard and privileged intents
GUILD_MESSAGE_REACTIONS (1 << 10) intent.Some intents are privileged, 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. 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.Choosing installation contexts
Now we’ll select where your app can be installed in Discord, which is determined by the installation contexts that your app supports.What are installation contexts?
Overview of where apps can be installed
What are installation contexts?
Overview of where apps can be installed
- Apps installed in a server context (server-installed apps) must be authorized by a server member with the MANAGE_GUILDpermission, and are visible to all members of the server.
- 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 installed to a user context are visible across all of the user’s servers, DMs, and GDMs—however, they’re limited to using commands.
Setting up an install link
Install links provide an easy way for users to install your app in Discord. We’ll set up the default Discord Provided Link, but you can read more about the different type of install links in the Application documentation. On the Installation page, 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.What are scopes and permissions?
Introduction to scopes and bot permissions
What are scopes and permissions?
Introduction to scopes and bot permissions
- OAuth2 Scopes determine what data access and actions your app can take, granted on behalf of an installing or authenticating user.
- Permissions 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. Since apps installed to a user context can only respond to commands, these permissions are only relevant to apps installed to a server.
- For User Install, add the applications.commandsscope
- For Guild Install, add the applications.commandsscope andbotscope. When you selectbot, 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 selectSend Messages.

Installing your app
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.Installing slash commands
node-fetch. You can see the implementation for the installation in utils.js within the DiscordRequest() function.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/<APP_ID>/commands endpoint.
If you want to customize your commands or add additional ones, you can reference the command structure in the commands documentation.
In your terminal within the project folder, run the following command:
What are Discord's APIs?
Overview of Discord's HTTP and Gateway APIs
What are Discord's APIs?
Overview of Discord's HTTP and Gateway APIs
- 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 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.
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 server, then use ngrok to expose our server publicly. First, go to your project’s folder and run the following to start your app: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 documentation.
PORT variable in your .env file.Adding an interaction endpoint URL
Go to your app’s settings and on the General Information page under Interaction Endpoint URL, paste your new ngrok forwarding URL and append/interactions.

- It uses the PUBLIC_KEYand discord-interactions package with a wrapper function (imported fromutils.js) that makes it conform to Express’sverifyinterface. This is run on every incoming request to your app.
- It responds to incoming PINGrequests.
Handling slash command requests
With the endpoint verified, navigate to your project’sapp.js file and find the code block that handles the /test command:
InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE is a constant exported from discord-interactions/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.
game.js file, feel free to poke around and change commands or the options in the commands.Handling the command interaction
Code for handling the challenge command and responding with a message containing a button
Handling the command interaction
Code for handling the challenge command and responding with a message containing a button
/challenge command, add the following code after the if name === “test” if block:examples/app.js.- 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 thememberobject. If it’s in a DM or Group DM, it will be in theuserobject.
- Adds a new game to the activeGamesobject using the interaction ID. The active game records theuserIdandobjectName.
- Sends a message back to the channel with a button with a custom_idofaccept_button_<SOME_ID>.
components array. Actionable components (like buttons) need to be inside of an 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.Handling button interactions
Code for handling button clicks and responding with an ephemeral message
Handling button interactions
Code for handling button clicks and responding with an ephemeral message
Handling select menu interactions
Code for responding to select menu interactions and updating the game state
Handling select menu interactions
Code for responding to select menu interactions and updating the game state