From the download to a bot that is online
You have the project. What is left is a Discord account, four screens in a settings page, and one command in a terminal. Every step says what you should be seeing when it worked, so you can tell where it went wrong instead of starting over.
- 01 · 1 minCreate the application
- 02 · 1 minGet the token
- 03 · 2 minTurn on the intents
- 04 · 3 minInvite it to your server
- 05 · 5 minRun it
About 15 minutes the first time. Steps 1 to 4 are the same for everyone. Step 5 depends on what you built.
Before you start
- The zip from your build, unzipped somewhere you can find again. No project yet? Build one first, the first one is free.
- A Discord account, and the Manage Server permission on the server you want the bot in. If it is not your server, the owner has to do step 4.
- For a normal bot, Node.js 18 or newer. Check with
node -vin a terminal. If that prints an error rather than a version, install Node first. - For a FiveM resource, access to your server files and to server.cfg.
Steps 1 to 4 happen on discord.com/developers/applications. Discord rearranges that page from time to time, so the labels below matter more than the exact position. Nothing here costs anything.
Create the application
1 minOpen the Developer Portal and press New Application in the top right. Give it a name and accept the terms.
The name is what people will see in the member list, so use the bot's name rather than your own. You can change it later.
Get the token
1 minOpen Bot in the left menu. Find Reset Token and press it. Discord will ask you to confirm, and may ask for your password or a two factor code.
A long string appears. This is shown once. Copy it now and paste it straight into the place it belongs, which is step 5. If you lose it, press Reset Token again and use the new one. There is no way to look up the old one, by design.
Turn on the intents it needs
2 minStay on the Bot page and scroll down to Privileged Gateway Intents. There are three switches, and the rule for all three is the same: on only if your bot actually uses it.
- Message Content
- The bot reads what people write in ordinary messages.
- Server Members
- The bot reacts to people joining or leaving, or reads the member list.
- Presence
- The bot cares who is online. Most do not.
Open the README in your project and turn on exactly the ones it lists. Every intent you enable is data Discord sends you and a claim about what the bot does, and past a hundred servers Discord reviews those claims. Slash commands need none of the three, so for many bots you change nothing here at all.
Press Save Changes. The bar at the bottom of the page has to disappear, or nothing was saved.
Invite it to your server
3 minOpen OAuth2 in the left menu, then URL Generator. In the Scopes box, tick two things:
bot, which lets it join at allapplications.commands, which lets it have slash commands
A Bot Permissions box appears underneath. Tick what the bot actually needs. A moderation bot needs the moderation permissions, a bot that only answers questions needs almost nothing. If you are unsure, start small: you can change the bot's permissions in your server later without re-inviting it.
Copy the link at the bottom, open it in a new tab, pick your server from the dropdown and confirm.
Run it
5 minThis is where the two kinds of project part ways. Pick the one you built. Not sure? Look inside the folder: a fxmanifest.lua means it is a FiveM resource, and no such file means it is a normal Node bot.
A normal Node bot
Install the dependencies
Open a terminal in the project folder, the one containing package.json, and run:
npm installnode_modules folder. Warnings during install are normal. Red text saying ERR! is not.Fill in .env
The project contains .env.example, which lists every value the code reads. Copy it to a file named exactly .env:
cp .env.example .env # macOS and Linux
copy .env.example .env # WindowsOpen it in any text editor and fill in every line. Paste each value after the equals sign, with no quotes and no spaces around it:
TOKEN=paste-the-token-here
CLIENT_ID=your-application-id
GUILD_ID=your-server-idYour file may name the first one TOKEN or DISCORD_TOKEN. Use whatever your own .env.example lists, do not rename it.
The two that catch people out. CLIENT_ID is the application id, not the token and not the bot's user id. It is on the General Information page of the portal, labelled Application ID. And GUILD_ID is your server's id.
Any id from Discord comes the same way: turn on Developer Mode under Settings, Advanced, then right click the server, channel or role and choose Copy ID. Ids are long numbers, nothing else.
.env next to package.json, with no value left empty.Build it
npm run builddist folder, and the command ended without listing errors.Register the commands
Slash commands are registered with Discord separately from the code that answers them. This is a one time step, repeated whenever you add, rename or remove a command.
npm run deployStart it
npm startKeep it online
Closing the terminal stops the bot. That is not a bug, it is what a running program is. The thing to understand about the obvious free options: a Discord bot holds one long lived connection and otherwise looks completely idle, and most free tiers sleep anything that looks idle.
- Your own machine. Fine for building and testing, online exactly as long as the terminal is open.
- A small always-on server. What most people end up on. A few dollars a month, or free on the tiers that hand you a real machine rather than a sleeping web service. Use a process manager so it restarts itself after a crash or a reboot.
- A managed platform. Connect a repository and it builds and runs the project for you. The least work, and the place to check the sleeping question first.
.env file, and never push it to the repository you are deploying from.A FiveM resource
Put the folder in resources
Copy the whole unzipped folder into your server's resources directory, the same place your other resources live. Keep it as one folder.
The folder name is the resource name and you will type it again in a moment, so if it has spaces or capitals in it, rename it now to something plain like my_discord_bot.
resources/my_discord_bot/fxmanifest.lua exists.Check that it is built
The download already contains a dist folder. We compile every resource before handing it over, so you normally do not have to. Confirm it is there:
dist/server.js exists inside the resource folder. That is the file fxmanifest.lua starts.If it is missing, or if you edit the TypeScript in src later, open a terminal in the resource folder and build it yourself:
npm install
npm run builddist/server.js the resource starts, reports success and does nothing at all. There is no error naming the cause, which is why this step is worth ten seconds.Put the token and the ids in server.cfg
FiveM does not use .env files. It uses convars, set in server.cfg. Open it, in txAdmin under the CFG editor or directly as a file, and add the lines your resource asks for. They are listed in its README and again in config.lua, which is a checklist rather than a file you edit:
set discord_bot_token "your-token-here"
set discord_application_id "123456789012345678"
set discord_guild_id "123456789012345678"
ensure my_discord_botThree things matter. The set lines must come above the ensure line, because the resource reads them as it starts. The name after ensure must match the folder name exactly. And the application id is not the token: it is the plain number on the General Information page of the portal, labelled Application ID.
Other ids, for channels and roles, come from Discord: turn on Developer Mode under Settings, Advanced, then right click and Copy ID.
exec secrets.cfg, and keep that file out of the repository.Restart and watch the console
Restart the server, a full restart rather than just the resource. Watch the server console while it comes up rather than watching Discord: the bot prints one line naming itself when it connects, prefixed with the resource name so you can find it among everything else.
Register the commands
Slash commands are registered separately from the code that answers them. Your resource ships a deploy script and its README says how to run it, which is worth reading rather than guessing here, because this step is the roughest part of the FiveM path.
npm run deploy from an ordinary terminal usually fails with GetConvar is not defined. That function only exists inside FXServer, so the script has to run there, where the convars you just set are readable. Some generated resources register their commands themselves on startup instead, in which case there is nothing to run.Nothing else to host. The bot lives and dies with the server it is in.
When it does not work
Nearly every failure at this stage is one of these, and none of them mean the code is wrong. Find your symptom and open it.
An invalid token was provided+
The token in your config is not the current one. Resetting a token in the portal kills the old one instantly, and Discord also kills any token it finds in a public repository, usually within minutes of the push.
Fix. Reset the token in the portal, paste the new one, and never commit it. If you pushed one to GitHub, reset it before doing anything else.
Used disallowed intents+
Your code asks for a privileged intent that is switched off for your application. The gateway refuses the whole connection rather than the one intent.
Fix. Portal, Bot, Privileged Gateway Intents. Turn on the ones your README lists, then Save Changes. Start the bot again.
The bot is online but the slash commands do not appear+
Almost always the invite link was missing the applications.commands scope. The bot can sit in the server and still have no permission to own commands there.
Fix. Generate a new invite link with both bot and applications.commands ticked, open it, and pick the same server. Then press Ctrl R in the Discord app to drop its cache.
The application did not respond+
A command took longer than three seconds to acknowledge. The interaction token dies at three seconds and there is no second chance.
Fix. This one is handled for you: generated handlers defer first and edit the reply afterwards. If you added a command by hand, do the same.
Commands registered but only in one server+
Guild commands appear instantly and only in the guild you named. Global commands reach everywhere but can take up to an hour.
Fix. Normal while testing. Set the guild id for instant updates, clear it for a real release and wait.
The bot goes offline after a while on a free hostNode+
Most free tiers sleep an idle process. A Discord bot holds a permanent gateway connection, so sleeping it means going offline.
Fix. Use a host that does not sleep. See the hosting section in step 5.
Still stuck? The report button on any page reaches us with the page you were on attached, which is faster than describing it.
Frequently Asked Questions
Discord Developer Portal, New Application, then the Bot tab, then Reset Token. The token is shown once. Copy it straight into your config and close the page.
Treat it exactly like a password. Anyone holding it controls the bot. If you ever paste it into a public repository, a screenshot or a support channel, reset it immediately, because Discord scans public code and will invalidate it anyway.
Intents tell Discord which categories of event to send you. Three of them are privileged and off by default: Message Content, Server Members and Presence.
You need one only if your bot actually uses it. Reading the text of ordinary messages needs Message Content. Reacting to people joining or leaving needs Server Members. Slash commands need none of the three. The README in your generated project lists the ones your particular bot asks for.
The usual reason is that the invite link did not include the applications.commands scope, so the bot is in the server but cannot own commands there. Re-invite it with both scopes ticked.
The second reason is timing: global commands can take up to an hour to appear everywhere, while guild commands appear instantly. The third is your own Discord client caching the old list, which Ctrl R fixes.
For a normal Node bot, yes, unless you put it somewhere else. The bot holds an open connection to Discord and is offline whenever that process is not running.
A FiveM resource is different. It runs inside the server you already pay for, starts when the server starts and needs no separate hosting at all.
For testing, yes. For a bot people rely on, be careful: most free tiers sleep a process that looks idle, and a bot holding a gateway connection looks idle to them, so it drops offline.
The free tiers that genuinely stay up are the ones giving you a real always-on machine rather than a sleeping web service. Otherwise the smallest paid instance anywhere is a few dollars a month.
Not in a .env file. FiveM uses convars, so it goes in your server.cfg as set discord_bot_token "your-token", and the resource reads it at startup.
Put that line above the ensure line for the resource. If your server.cfg is in a public repository or shared with anyone, keep the token in a separate cfg file that you exec and do not share.
Two signs, both in the console rather than in Discord. The bot prints one line naming itself when it connects, and the member list in your server shows it as online.
For a FiveM resource the same line goes to the server console, so watch the console right after the resource starts rather than looking at Discord first.
For a Node bot: npm run build, then start it again. If you added, removed or renamed a command, also run npm run deploy, because the list Discord shows is registered separately from the code that answers.
For a FiveM resource: restart the resource. If you changed the commands, the deploy step still applies.
Do not have the code yet?
Describe what the bot should do and you get the project this guide sets up. The first build is free.