Skip to main content
Slash commands are Discord’s native command interface, providing a clean and user-friendly way for users to interact with your bot.

Basic Slash Command

Create a simple slash command using the @bot.slash_command() decorator:
import discord

bot = discord.Bot()

@bot.slash_command(guild_ids=[...])  # Remove guild_ids to make it global
async def hello(ctx: discord.ApplicationContext):
    """Say hello to the bot"""  # This becomes the command description
    await ctx.respond(f"Hello {ctx.author}!")

bot.run("TOKEN")
Interactions must be responded to within 3 seconds. If your command takes longer, use await ctx.defer() first.

Command Options

Using the Option Class

Add parameters to your slash commands using the Option class:
from discord import option

@bot.slash_command()
@option("name", description="Enter your name")
@option(
    "pokemon",
    description="Choose your starter Pokémon",
    choices=["Bulbasaur", "Squirtle", "Charmander", "Pikachu"],
)
@option(
    "age",
    description="Enter your age",
    min_value=1,
    max_value=99,
    default=18,  # Makes the argument optional
)
async def hello(
    ctx: discord.ApplicationContext,
    name: str,
    pokemon: str,
    age: int,
):
    await ctx.respond(
        f"Hello {name}! Your starter is {pokemon} and you are {age} years old."
    )

Option Types

Pycord supports various option types:
@option("text", str, description="Enter some text")

Working with Attachments

@bot.slash_command()
@option("attachment", discord.Attachment, description="A file to upload")
async def upload(ctx: discord.ApplicationContext, attachment: discord.Attachment):
    if attachment:
        file = await attachment.to_file()
        await ctx.respond("Here's your file!", file=file)
    else:
        await ctx.respond("You didn't provide a file!")

Autocomplete

Add dynamic suggestions to your command options:

Basic Autocomplete

async def get_colors(ctx: discord.AutocompleteContext):
    """Returns a list of colors that match the user's input."""
    colors = ["red", "orange", "yellow", "green", "blue", "indigo", "violet"]
    return [color for color in colors if color.startswith(ctx.value.lower())]

@bot.slash_command()
@option("color", description="Pick a color", autocomplete=get_colors)
async def favorite(ctx: discord.ApplicationContext, color: str):
    await ctx.respond(f"Your favorite color is {color}!")

Context-Aware Autocomplete

Access other option values to create dependent autocomplete:
async def get_animals(ctx: discord.AutocompleteContext):
    """Returns animals based on the selected color."""
    picked_color = ctx.options["color"]
    if picked_color == "red":
        return ["cardinal", "ladybug"]
    elif picked_color == "orange":
        return ["clownfish", "tiger"]
    elif picked_color == "blue":
        return ["blue jay", "blue whale"]
    return ["rainbowfish"]

@bot.slash_command()
@option("color", description="Pick a color", autocomplete=get_colors)
@option("animal", description="Pick an animal", autocomplete=get_animals)
async def combo(ctx: discord.ApplicationContext, color: str, animal: str):
    await ctx.respond(f"You picked {color} {animal}!")

Using basic_autocomplete Helper

@bot.slash_command()
@option(
    "animal",
    description="Pick an animal",
    autocomplete=discord.utils.basic_autocomplete(["snail", "python", "cricket", "orca"]),
)
async def animal(ctx: discord.ApplicationContext, animal: str):
    await ctx.respond(f"You chose {animal}!")
Autocomplete functions can return up to 25 choices at a time.

Autocomplete with OptionChoice

async def food_autocomplete(ctx: discord.AutocompleteContext):
    items = ["Apple", "Banana", "Orange"]
    return [
        discord.OptionChoice(name=item)
        for item in items
        if ctx.value.lower() in item.lower()
    ]

Command Groups

Organize related commands into groups:

Creating a Command Group

math = bot.create_group("math", "Commands related to mathematics")

@math.command()
async def add(ctx: discord.ApplicationContext, num1: int, num2: int):
    """Get the sum of 2 integers."""
    await ctx.respond(f"The sum is **{num1 + num2}**")

@math.command()
async def multiply(ctx: discord.ApplicationContext, num1: int, num2: int):
    """Get the product of 2 integers."""
    await ctx.respond(f"The product is **{num1 * num2}**")

Alternative Group Creation

math = discord.SlashCommandGroup("math", "Commands related to mathematics")

@math.command()
async def subtract(ctx: discord.ApplicationContext, num1: int, num2: int):
    """Get the difference of 2 integers."""
    await ctx.respond(f"The difference is **{num1 - num2}**")

bot.add_application_command(math)

Nested Subcommands

admin = bot.create_group("admin", "Admin commands")
user_group = admin.create_subgroup("user", "User management")

@user_group.command()
async def ban(ctx: discord.ApplicationContext, member: discord.Member):
    """Ban a user."""
    await ctx.respond(f"Banned {member.mention}")

@user_group.command()
async def kick(ctx: discord.ApplicationContext, member: discord.Member):
    """Kick a user."""
    await ctx.respond(f"Kicked {member.mention}")

Command Permissions

Default Member Permissions

from discord import default_permissions

@bot.slash_command()
@default_permissions(administrator=True)
async def setup(ctx: discord.ApplicationContext):
    """Only administrators can use this command."""
    await ctx.respond("Setting up...")

Guild-Only Commands

@bot.slash_command(contexts={discord.InteractionContextType.guild})
async def guild_only(ctx: discord.ApplicationContext):
    """This command only works in servers."""
    await ctx.respond(f"Server: {ctx.guild.name}")

Response Types

Basic Response

await ctx.respond("Hello!")

Ephemeral Response

await ctx.respond("Only you can see this!", ephemeral=True)

Deferred Response

@bot.slash_command()
async def slow(ctx: discord.ApplicationContext):
    await ctx.defer()  # Show "Bot is thinking..."
    # Do something that takes time
    await asyncio.sleep(5)
    await ctx.followup.send("Done!")

Response with Embeds

@bot.slash_command()
async def info(ctx: discord.ApplicationContext):
    embed = discord.Embed(
        title="Bot Info",
        description="This is my bot!",
        color=discord.Color.blue()
    )
    embed.add_field(name="Version", value="1.0")
    await ctx.respond(embed=embed)

Best Practices

1
Use descriptive names
2
Command and option names should be clear and lowercase with hyphens.
3
Add descriptions
4
Always provide descriptions for commands and options. They appear in the Discord UI.
5
Handle errors gracefully
6
Use try-except blocks to catch and handle errors in your commands.
7
Use defer for long operations
8
If your command takes more than 3 seconds, defer the response.
9
Validate input
10
Check option values before using them to prevent errors.
Common Mistakes:
  • Forgetting to respond to an interaction within 3 seconds
  • Trying to respond to the same interaction twice
  • Using spaces in command/option names (use hyphens instead)
  • Not setting guild_ids during development (causes slow global command updates)

See Also

Build docs developers (and LLMs) love