Every Discord bot needs an application in the Discord Developer Portal and a token it logs in with. We'll show you how to create the application, which intents to turn on, how to invite the bot to a server and how to store the token so it never leaks.
What you'll need
- A Discord account with a verified email
- A Discord server where you have the Manage Server permission (create your own for testing)
- About 10 minutes
Creating the application#
Open the Discord Developer Portal (opens in a new window) and log in.
Click New Application.
Enter a name (for example
MujBot), accept the terms and click Create.The General Information page opens. You can set an icon and a description here.
Copy the Application ID. It's also called the Client ID and you'll need it to register slash commands. It isn't secret.
Bot token#
The token is the bot's password. Anyone who has it can control the bot: send messages, delete channels and ban people on every server the bot is on.
- In the application's left menu, click Bot.
- In the Token section, click Reset Token and confirm. Discord may ask for your password or a two-factor authentication code.
- Copy the token (Copy) and store it right away (see “Storing the token”).
The token is shown only once. If you lose it, generate a new one with Reset Token. That invalidates the old token.
Never put the token in code or on GitHub
Don't write the token directly into a code file, and don't send it on Discord, in a screenshot or to support. If it leaks, click Reset Token right away and put the new token in .env on the server. The bot using the old token gets disconnected.
Don't mix them up: the short Client Secret string on the OAuth2 page isn't the bot token. The token is longer and consists of three parts separated by dots.
Privileged Gateway Intents#
Intents tell Discord which events to send to the bot. Most are freely available, but three are privileged and you have to turn them on on the Bot page in the Privileged Gateway Intents section:
| Intent | What the bot gets | When to turn it on |
|---|---|---|
| Presence Intent | Member status (online, playing a game, custom status). | Only when the bot really uses member status (activity statistics). |
| Server Members Intent | The server's member list and member join and leave events. | Welcome messages, automatic roles on join, working with the member list. |
| Message Content Intent | The text, attachments and embeds of regular messages. | Only when the bot reads regular messages (prefix commands like !help, text moderation). Slash commands don't need it. |
- Turn on only what the bot needs. An intent has to be enabled in the Developer Portal and requested in code (
GatewayIntentBitsin discord.js). - If the code requests an intent that isn't enabled in the Developer Portal, Discord closes the connection and the bot reports
Used disallowed intents. - A bot on 100 or more servers has to go through app verification, and Discord has to approve its privileged intents. When more than 10,000 users can see the application, Discord requires an application and a review for continued use of privileged intents, which then has to be renewed every year.
Without Message Content Intent, the bot only sees the text of messages that mention it, of direct messages with it and of messages it sent itself.
Inviting the bot to a server#
In the application's left menu, click Installation.
In the Installation Contexts section, keep Guild Install checked (installing to a server). You only need User Install for commands that users add to their own account.
In the Install Link section, choose Discord Provided Link.
In the Default Install Settings section, under Guild Install, add
applications.commandsandbotto Scopes.bot– adds the bot user to the server.applications.commands– lets the bot create slash commands on the server.
After you add
bot, Permissions appears. Pick only the permissions the bot needs (see the table below) and save the changes.Copy the link from Install Link and open it in your browser.
Choose Add to server, select the server and confirm with Authorize.
The bot appears in the server's member list (offline until you start it).
| Permission | What the bot needs it for |
|---|---|
| View Channels | Seeing the channels it works in. |
| Send Messages | Sending messages. |
| Embed Links | Sending embeds (messages with a frame and a title). |
| Read Message History | Reading older messages (replies, cleanup). |
| Manage Roles | Only when the bot assigns roles. It can only manage roles below its own highest role. |
| Administrator | Don't grant it. A bot with this permission can do anything on the server. If the token leaks, it's a disaster. |
You can also create the link manually on the OAuth2 page in the OAuth2 URL Generator section: check bot and applications.commands, pick the permissions and copy the generated URL.
Storing the token#
The token belongs in a .env file, not in your code. Create .env directly on the server so you don't have to move the token anywhere:
In the customer area, open the bot and go to the Files tab, to the root folder
/.Click New file and name it
.env(with a dot at the start, no extension).Type this in the editor:
.envini DISCORD_TOKEN=<BOT_TOKEN> CLIENT_ID=<APPLICATION_ID> GUILD_ID=<SERVER_ID>DISCORD_TOKEN– the bot token. Replace<BOT_TOKEN>with the token from the Developer Portal, without quotes and without spaces around=.CLIENT_ID– the Application ID from the General Information page.GUILD_ID– the ID of your Discord server (in Discord, turn on User Settings › Advanced › Developer Mode, then right-click the server name › Copy Server ID).
Save with Ctrl+S.
Loading .env in code#
Node.js doesn't load .env on its own; you have to tell it to. Since you can't change the startup command on our servers, load .env directly in your code. The most reliable way is the dotenv package (install it on your computer with npm install dotenv):
const path = require('node:path');
require('dotenv').config({ path: path.join(__dirname, '.env'), quiet: true });
const token = process.env.DISCORD_TOKEN;require(– loads the variables from the file into'dotenv'). config({ … }) process.env. It has to be at the very top, before any code that uses the token.path: path.join(__dirname, '.env')– looks for.envin the same folder asindex.js, no matter where the bot is started from.quiet: true– doesn't print a message about loading the file at startup.process.– the value fromenv. DISCORD_ TOKEN .env. It's always text (a string).
For ES modules ("type": "module" in package.json), use import 'dotenv/config'; on the first line.
Since Node.js 20.12, you can do without the package and use the built-in process.loadEnvFile() function:
process.loadEnvFile(require('node:path').join(__dirname, '.env'));If the .env file doesn't exist, this function throws an error. To find out your Node.js version, see Node.js version. On your computer, you can also start the bot with node --env-file=.env index.js.
The .gitignore file#
If you keep your code in Git (GitHub, GitLab), create a .gitignore file in the project:
node_modules
.envGit will then never upload .env or node_modules. If you've already pushed .env by mistake, deleting it isn't enough (it stays in the history). Reset the token right away.
Other secrets#
Other passwords and keys belong in .env too: the database password, API keys for other services, webhooks. The same rules apply:
- in code, use only
process.env.NAME, never the value itself, - never print them to the console (a member of a shared service can see the console too),
- if they leak, revoke them right away with the service that issued them.
Verification#
- After you start the bot, it goes online on Discord and the Console doesn't show
An invalid token was provided. - To check that the token loaded, print only its length, never the token itself:
console.log('Token length:', process.. If it printsenv. DISCORD_ TOKEN?. length); undefined,.envdidn't load.
Troubleshooting#
An invalid token was provided.#
The token is wrong, stopped working after Reset Token, or didn't load at all. Check the token length (see Verification), the variable name in .env and in your code (DISCORD_TOKEN), and that .env has no extra quotes or spaces. If you're not sure, generate a new token and add it again.
Used disallowed intents#
The code requests a privileged intent that isn't enabled in the Developer Portal. On the Bot page, under Privileged Gateway Intents, turn on the intent, or remove it from the code.
process.env.DISCORD_TOKEN is undefined#
dotenvis loaded after the code that uses the token. Putrequire(on the first lines.'dotenv'). config( …) - The file is actually named
.env.txt(Windows hides file extensions). Rename it to.envon the server. .envis in a different folder than the main file. Usepath.join(__dirname, '.env').
Private application cannot have a default authorization link#
You turned off Public Bot on the Bot page, but Discord Provided Link is set on the Installation page. A private bot can't have a default link. Either keep Public Bot on, or choose None in Install Link and create the link in OAuth2 URL Generator.
Bot requires a code grant#
Requires OAuth2 Code Grant is turned on on the Bot page. Unless you specifically need it, turn it off.
I can't see my server in the list when inviting the bot#
You don't have the Manage Server permission on that server. Ask the server owner, or invite the bot to your own server.
Summary
- You create the application in the Developer Portal (New Application) and get the token on the Bot page with Reset Token. It's shown only once.
- Turn on privileged intents (Presence, Server Members, Message Content) only when the bot needs them, both in the Developer Portal and in code.
- Invite the bot with the link from the Installation page, with the
botandapplications.commandsscopes and as few permissions as possible, never Administrator. - The token belongs in
.envon the server. Load it in code withdotenvand add.envto.gitignore.