Skip to content

June 23, 2026

Building Discord Slash Commands — An Application Commands Implementation Guide

CommunityDiscordEngineeringAI

Slash Commands Turn Discord Into an Operator’s Control Panel

As covered in What You Can Do with Discord Bots and APIs, the entry point for Discord automation is making messages interactive. Slash commands (Application Commands) are the most important part of that.

Type /help and an FAQ appears. Run /report @user and moderators get a notification. Execute /event and an event list appears with buttons. None of this happens automatically — you have to design it, implement it, and register it before anything works.

This article explains how slash commands work and walks through implementation in TypeScript with working code examples.

How Slash Commands Work

Slash commands are defined using the Discord Application Commands API. When a user runs a command, Discord sends an Interaction to your bot.

User executes /hello
 → Discord sends an Interaction to the bot (or a Worker URL)
 → Bot responds with JSON (reply message, Embed, button, etc.)
 → Discord displays the response

There are two ways to receive and respond to Interactions.

ApproachHow It WorksBest For
Gateway (WebSocket)Always-on connection, receives all eventsReaction monitoring, processing all messages
Interactions Endpoint (HTTP)Receives HTTP POST requestsCommands, buttons, modals

Slash commands work with the HTTP approach (Interactions Endpoint), which can run serverless. See Running a Discord Bot Serverlessly on Cloudflare for the full setup.

Command Types

There are three types of Application Commands.

TypeDescriptionExample
Slash command (CHAT_INPUT)Executed via /command-name/help, /report
User context menuExecuted by right-clicking a user”Report this user”
Message context menuExecuted by right-clicking a message”Summarize this message”

Slash commands are by far the most common. This article focuses on them.

Implementation: Building Commands with discord.js v14

Prerequisites

npm install discord.js @discordjs/rest discord-api-types

Step 1: Define the Command

// src/commands/hello.ts
import { SlashCommandBuilder } from 'discord.js';

export const helloCommand = new SlashCommandBuilder()
  .setName('hello')
  .setDescription('The bot greets you back')
  .addStringOption((option) =>
    option
      .setName('name')
      .setDescription('Name to greet')
      .setRequired(false)
  );

SlashCommandBuilder lets you define the name, description, and arguments. Argument types include strings (addStringOption), integers (addIntegerOption), user mentions (addUserOption), and booleans (addBooleanOption).

Step 2: Register the Command

// scripts/register-commands.ts
import { REST, Routes } from 'discord.js';
import { helloCommand } from '../src/commands/hello';

const token = process.env.DISCORD_BOT_TOKEN!;
const clientId = process.env.DISCORD_CLIENT_ID!;
const guildId = process.env.DISCORD_GUILD_ID; // dev only

const rest = new REST({ version: '10' }).setToken(token);
const commands = [helloCommand.toJSON()];

async function main() {
  if (guildId) {
    // Guild command — instant update, dev/test use
    await rest.put(
      Routes.applicationGuildCommands(clientId, guildId),
      { body: commands }
    );
    console.log('Guild commands registered.');
  } else {
    // Global command — up to 1h propagation, production use
    await rest.put(
      Routes.applicationCommands(clientId),
      { body: commands }
    );
    console.log('Global commands registered.');
  }
}

main();

During development, use guild commands for instant feedback. Switch to global registration when deploying to production.

Step 3: Respond to Interactions

// src/index.ts
import { Client, GatewayIntentBits, Events, EmbedBuilder } from 'discord.js';

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

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

  if (interaction.commandName === 'hello') {
    const name = interaction.options.getString('name') ?? 'everyone';

    const embed = new EmbedBuilder()
      .setColor(0x5865f2) // Discord Blurple
      .setTitle(`Hello, ${name}!`)
      .setDescription('Welcome to the community. Feel free to ask if you need anything.')
      .setTimestamp();

    await interaction.reply({ embeds: [embed] });
  }
});

client.login(process.env.DISCORD_BOT_TOKEN);

Use interaction.reply() for immediate responses. If processing takes more than 3 seconds, call await interaction.deferReply() first, then interaction.editReply() once the work is done.

Common Command Patterns for Community Operations

Pattern 1: Moderation Notification

// /report @user [reason] — forwards report to moderator channel
.setName('report')
.addUserOption(opt => opt.setName('user').setDescription('User to report').setRequired(true))
.addStringOption(opt => opt.setName('reason').setDescription('Reason').setRequired(false))

Accepts reports from members and forwards them as an Embed to the moderator channel.

Pattern 2: FAQ Response

// /faq [topic] — answers common questions
.setName('faq')
.addStringOption(opt =>
  opt.setName('topic')
    .setDescription('What do you need help with?')
    .addChoices(
      { name: 'How to join', value: 'join' },
      { name: 'Events', value: 'events' },
      { name: 'Rules', value: 'rules' }
    )
)

addChoices creates a dropdown with autocomplete. Pair this with an LLM for free-text Q&A.

Pattern 3: Role Request

// /role @role-name — self-service role assignment
.setName('role')
.addRoleOption(opt =>
  opt.setName('name').setDescription('Role you want').setRequired(true)
)

Validates against an allowlist and automatically assigns roles based on self-declaration.

Vibe-Coding Slash Commands

When implementing the above with AI coding tools (Claude, etc.), here is an effective prompt structure:

I want to build a Discord slash command in TypeScript.
Command name: /report
Arguments: user (user mention, required), reason (string, optional)
Behavior:
  - Reply to the executor with an Ephemeral "Report received"
  - Post an Embed notification to #moderators (reporter, target, reason, timestamp)

Library: discord.js v14
Please include both the command registration script and bot startup file.

Always verify the generated code for:

  • Signature verification (if using Interactions Endpoint)
  • Error handling (channel not found, insufficient permissions, etc.)
  • Rate limit awareness in the design

If you don’t understand a part of the code, ask “explain why this part is needed” before putting it into production.

Permission Design: Who Can Use Which Command

Set defaultMemberPermissions during command creation to restrict execution to members with specific permissions.

new SlashCommandBuilder()
  .setName('ban')
  .setDescription('Bans a user')
  .setDefaultMemberPermissions(PermissionFlagsBits.BanMembers) // only users with BAN_MEMBERS
  .addUserOption(opt =>
    opt.setName('user').setDescription('Target user').setRequired(true)
  )

Server administrators can further customize per-command permissions from “Server Settings → Integrations → Bot App.”

Summary

  • Slash commands are registered with the Discord Application Commands API and respond via Interactions
  • Use guild commands (instant) during development; switch to global registration for production
  • Arguments support types, choices, and required/optional flags — autocomplete comes standard
  • Moderation notifications, FAQ responses, and role requests are the most common community use cases
  • For vibe-coding, be explicit about command name, arguments, behavior, and library
  • defaultMemberPermissions controls who can run each command at the code level

Once your slash commands are working, the next step is combining buttons and modals to build more complex interactive flows.


Looking for help designing and implementing Slack or Discord bots for your community? See Rokuse’s Community Development Services.

Contact · Rokuse LLC

Continue this conversation about your community.

If a moment in this article made you wonder "what about ours?", send that exact question. It does not have to be polished — we will work the entry point out together.

Frequently asked questions

Q. What is the difference between slash commands and prefix commands like !help?
A. Slash commands are registered as official Discord Application Commands, which means you get autocomplete, permission control, typed arguments, and localization out of the box. Prefix commands (like !help) rely on the bot matching text in a message — no autocomplete, limited permission control. Discord officially recommends migrating to slash commands, and all new implementations should use them.
Q. How long does it take for slash commands to appear after registration?
A. Global commands (visible in all servers) can take up to one hour to propagate. During development and testing, use guild commands (server-specific) for instant visibility. In production, hook global registration into your deploy pipeline so commands update automatically whenever your code changes.
Q. What should I tell an AI when vibe-coding a slash command?
A. I want to build a Discord slash command in TypeScript. Command name /report, arguments text (string, required). On execution, return a confirmation as an Ephemeral message and forward the report to a specific channel as an Embed. Use discord.js v14 and include both the command registration script and the bot startup file. Being explicit about the command name, argument types, response format, and library gets you high-quality code.
Q. How do I restrict which users can run a slash command?
A. Set defaultMemberPermissions when building the command to limit execution to members with a specific permission — for example, PermissionFlagsBits.BanMembers for a /ban command restricted to moderators. Server administrators can further override permissions per command through the Discord "Integrations" settings.