Discord Bots

Adding slash commands

CanerAkar Mar 17, 2026

Slash commands are the modern way for users to interact with Discord bots. When a user types / in a Discord channel, they see a list of available commands with descriptions and parameters. This guide shows you how to add slash commands to your bot hosted on FPS.ms.

Why use slash commands?

FeatureSlash commandsMessage-based commands
DiscoverabilityUsers see all commands by typing `/`Users must know the prefix and command name
PermissionsBuilt-in Discord permission systemMust be coded manually
ParametersTyped parameters with validationMust parse text manually
Message Content intent**Not required**Required (privileged intent)
Recommended for new bots
Discord recommends slash commands for all new bots. They don't require the Message Content privileged intent, making your bot easier to verify and approve.

Prerequisites

Before adding slash commands, make sure you:
  1. Have your bot installed and running on FPS.ms
  2. Added the applications.commands scope when inviting your bot
  3. Have your bot token set up as an environment variable
Missing applications.commands scope?
If you didn't include applications.commands when inviting your bot, slash commands won't work. Re-invite the bot using the OAuth2 URL Generator in the Discord Developer Portal with both bot and applications.commands scopes selected.

Python (discord.py)

Discord.py uses a commands.Bot with a command tree to handle slash commands.

Basic slash command

app.py
import os
from dotenv import load_dotenv
import discord
from discord import app_commands

load_dotenv()

class MyBot(discord.Client):
    def __init__(self):
        super().__init__(intents=discord.Intents.default())
        self.tree = app_commands.CommandTree(self)

    async def setup_hook(self):
        await self.tree.sync()
        print(f'Synced {len(self.tree.get_commands())} commands')

bot = MyBot()

@bot.tree.command(name='ping', description='Check if the bot is online')
async def ping(interaction: discord.Interaction):
    await interaction.response.send_message(f'Pong! Latency: {round(bot.latency * 1000)}ms')

@bot.tree.command(name='hello', description='Get a greeting from the bot')
@app_commands.describe(name='The name to greet')
async def hello(interaction: discord.Interaction, name: str = None):
    if name:
        await interaction.response.send_message(f'Hello, {name}!')
    else:
        await interaction.response.send_message(f'Hello, {interaction.user.display_name}!')

bot.run(os.environ['BOT_TOKEN'])

How it works

  1. CommandTree — manages all your slash commands
  2. @bot.tree.command() — registers a new slash command with a name and description
  3. @app_commands.describe() — adds descriptions to command parameters
  4. setup_hook() — syncs commands with Discord when the bot starts
  5. interaction.response.send_message() — replies to the user

Node.js (discord.js)

Discord.js uses a two-step process: register commands via the REST API, then handle them in your bot code.

Step 1: Register commands

Create a separate script to register your commands with Discord:
deploy-commands.js
require('dotenv').config();
const { REST, Routes, SlashCommandBuilder } = require('discord.js');

const commands = [
    new SlashCommandBuilder()
        .setName('ping')
        .setDescription('Check if the bot is online'),
    new SlashCommandBuilder()
        .setName('hello')
        .setDescription('Get a greeting from the bot')
        .addStringOption(option =>
            option.setName('name')
                .setDescription('The name to greet')
                .setRequired(false)),
].map(command => command.toJSON());

const rest = new REST().setToken(process.env.BOT_TOKEN);

(async () => {
    try {
        console.log('Registering slash commands...');
        await rest.put(
            Routes.applicationCommands(process.env.CLIENT_ID),
            { body: commands },
        );
        console.log('Slash commands registered!');
    } catch (error) {
        console.error(error);
    }
})();
Run this script once to register your commands:
node deploy-commands.js
Set CLIENT_ID
Add CLIENT_ID=your-app-id to your .env file on FPS.ms. Find it in the Discord Developer Portal under your application's General Information page (called Application ID). See the environment variables guide for details.

Step 2: Handle commands

index.js
require('dotenv').config();
const { Client, Events, GatewayIntentBits } = require('discord.js');

const client = new Client({ intents: [GatewayIntentBits.Guilds] });

client.on(Events.InteractionCreate, async interaction => {
    if (!interaction.isChatInputCommand()) return;

    if (interaction.commandName === 'ping') {
        await interaction.reply(`Pong! Latency: ${Math.round(client.ws.ping)}ms`);
    }

    if (interaction.commandName === 'hello') {
        const name = interaction.options.getString('name');
        if (name) {
            await interaction.reply(`Hello, ${name}!`);
        } else {
            await interaction.reply(`Hello, ${interaction.user.displayName}!`);
        }
    }
});

client.once(Events.ClientReady, readyClient => {
    console.log(`Ready! Logged in as ${readyClient.user.tag}`);
});

client.login(process.env.BOT_TOKEN);

Global vs guild commands

TypeRegistration timeScopeBest for
**Global**Up to 1 hourAll servers the bot is inProduction bots
**Guild**InstantOne specific serverDevelopment and testing
For faster testing, register commands to a specific guild. In Python:
MY_GUILD = discord.Object(id=int(os.environ['GUILD_ID']))

async def setup_hook(self):
    self.tree.copy_global_to(guild=MY_GUILD)
    await self.tree.sync(guild=MY_GUILD)
In Node.js, change the Routes in deploy-commands.js:
// Global (slow, production)
Routes.applicationCommands(process.env.CLIENT_ID)

// Guild-specific (instant, development)
Routes.applicationGuildCommands(process.env.CLIENT_ID, process.env.GUILD_ID)

Troubleshooting

ProblemCauseFix
Commands not showing upNot synced or registration delayFor global commands, wait up to 1 hour. For guild commands, check the guild ID.
`Missing Access` errorBot missing `applications.commands` scopeRe-invite the bot with the correct scopes
`Unknown interaction`Bot took too long to respond (>3 seconds)Use `interaction.deferReply()` for slow operations
Duplicate commandsSynced both global and guild copiesRemove guild commands if using global, or vice versa
For more help, see our full troubleshooting guide.

Next steps

Was this article helpful?