Skip to main content
This guide will walk you through creating your first Discord bot using JDA. By the end, you’ll have a working bot that responds to slash commands.

Prerequisites

1

Install JDA

Make sure you’ve installed JDA in your project using Gradle or Maven.
2

Create a Discord Application

You’ll need to create a bot account in the Discord Developer Portal.
  1. Click “New Application” and give it a name
  2. Navigate to the “Bot” section
  3. Click “Add Bot”
  4. Copy your bot token (keep this secret!)
  5. Enable the required intents for your bot
Never share your bot token publicly. It provides full access to your bot.
3

Invite Your Bot

Generate an invite link in the OAuth2 section:
  1. Go to OAuth2 > URL Generator
  2. Select bot and applications.commands scopes
  3. Select the required bot permissions
  4. Copy the generated URL and open it in your browser
  5. Select a server and authorize the bot

Your First Bot: Slash Commands

Let’s create a simple bot that responds to slash commands. This example uses no intents, making it lightweight and easy to start with.
1

Create the Bot Class

Create a new file SlashBot.java:
SlashBot.java
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
import net.dv8tion.jda.api.interactions.commands.build.Commands;
import net.dv8tion.jda.api.requests.GatewayIntent;

import java.util.EnumSet;

import static net.dv8tion.jda.api.interactions.commands.OptionType.STRING;

public class SlashBot extends ListenerAdapter {
    public static void main(String[] args) {
        // Slash commands don't need any intents
        EnumSet<GatewayIntent> intents = EnumSet.noneOf(GatewayIntent.class);
        
        JDA jda = JDABuilder.createLight("YOUR_BOT_TOKEN", intents)
                .addEventListeners(new SlashBot())
                .build();

        // Register slash commands
        jda.updateCommands().addCommands(
            Commands.slash("say", "Makes the bot say what you tell it to")
                .addOption(STRING, "content", "What the bot should say", true)
        ).queue();
    }

    @Override
    public void onSlashCommandInteraction(SlashCommandInteractionEvent event) {
        if (event.getName().equals("say")) {
            String content = event.getOption("content", OptionMapping::getAsString);
            event.reply(content).queue();
        }
    }
}
Replace YOUR_BOT_TOKEN with your actual bot token from the Discord Developer Portal.
2

Run Your Bot

Compile and run your bot:
javac -cp "path/to/JDA.jar" SlashBot.java
java -cp ".:path/to/JDA.jar" SlashBot
If you’re using an IDE, simply run the main method.
3

Test Your Bot

In Discord, type /say and you should see your command appear. Enter some text and the bot will repeat it back to you!
If you don’t see the command immediately, try reloading Discord (Ctrl+R on desktop).

Understanding the Code

Let’s break down what’s happening:

JDABuilder

JDA jda = JDABuilder.createLight("YOUR_BOT_TOKEN", intents)
    .addEventListeners(new SlashBot())
    .build();
  • createLight() creates a lightweight JDA instance with minimal caching
  • addEventListeners() registers your event listener class
  • build() starts the bot and connects to Discord

Registering Commands

jda.updateCommands().addCommands(
    Commands.slash("say", "Makes the bot say what you tell it to")
        .addOption(STRING, "content", "What the bot should say", true)
).queue();
This registers a /say command with one required string parameter called content.

Handling Commands

@Override
public void onSlashCommandInteraction(SlashCommandInteractionEvent event) {
    if (event.getName().equals("say")) {
        String content = event.getOption("content", OptionMapping::getAsString);
        event.reply(content).queue();
    }
}
This method is called whenever someone uses a slash command. We check the command name and respond accordingly.

Example: Message Logger Bot

For a more complex example, here’s a bot that logs messages to the console:
MessageLogger.java
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import net.dv8tion.jda.api.requests.GatewayIntent;

import java.util.EnumSet;

public class MessageLogger extends ListenerAdapter {
    public static void main(String[] args) {
        // This bot needs the MESSAGE_CONTENT privileged intent
        EnumSet<GatewayIntent> intents = EnumSet.of(
            GatewayIntent.GUILD_MESSAGES,
            GatewayIntent.MESSAGE_CONTENT
        );

        JDABuilder.createLight("YOUR_BOT_TOKEN", intents)
            .addEventListeners(new MessageLogger())
            .build();
    }

    @Override
    public void onMessageReceived(MessageReceivedEvent event) {
        User author = event.getAuthor();
        Message message = event.getMessage();
        
        if (event.isFromGuild()) {
            System.out.printf("[%s] [%#s] %#s: %s\n",
                event.getGuild().getName(),
                event.getChannel(),
                author,
                message.getContentDisplay());
        } else {
            System.out.printf("[direct] %#s: %s\n", 
                author, 
                message.getContentDisplay());
        }
    }
}
The MESSAGE_CONTENT intent is privileged and must be explicitly enabled in your bot settings in the Discord Developer Portal.

Gateway Intents

Intents control what events your bot receives. For optimal performance, only enable the intents you need:
  • GUILD_MESSAGES - Receive message events in servers
  • DIRECT_MESSAGES - Receive direct message events
  • MESSAGE_CONTENT - Access message content (privileged)
  • GUILD_MEMBERS - Access member information (privileged)
  • GUILD_PRESENCES - Access presence updates (privileged)
Read more about Gateway Intents and Member Cache Policy to understand how to configure your bot properly.

RestAction and Queuing

All API requests in JDA use the RestAction interface:
channel.sendMessage("Hello!")
    .queue(); // Sends the request asynchronously
You must call queue(), submit(), or complete() to execute the request. Without it, nothing happens!

RestAction Methods

  • queue() - Execute asynchronously (recommended)
  • queue(success -> {...}) - Execute with a success callback
  • submit() - Returns a CompletableFuture<T>
  • complete() - Execute synchronously (blocking)

Chaining Actions

event.reply("Processing...")
    .flatMap(hook -> hook.editOriginal("Done!"))
    .queue();

Next Steps

RestAction Guide

Learn how to chain and combine API requests

Interactions

Build buttons, select menus, and modals

Event List

Explore all available events

Examples

Browse more code examples

Build docs developers (and LLMs) love