On this page

How to Set Up OpenClaw on Discord (Bot Setup, Permissions, Slash Commands)

Discord is where communities live. Gaming guilds, developer hangouts, study groups, NFT projects. If your community is on Discord, your AI agent should be too.

Unlike Telegram (simple bot setup) or WhatsApp (complicated approval process), Discord sits in the middle: powerful bot features but requires understanding permissions and intents. This guide walks you through creating a Discord bot from scratch, connecting it to OpenClaw, and avoiding the #1 mistake that breaks 80% of new bots — forgetting to enable gateway intents.

Why Discord Bots Are Different

Discord’s approach: bots are first-class citizens. Discord wants bots — excellent docs, generous rate limits, rich features. The catch is complexity. Every Discord bot needs:

  • Application — registered in Discord Developer Portal
  • Bot user — the actual bot account
  • OAuth2 scopes — what the bot can access
  • Permissions — what the bot can do
  • Gateway intents — which events the bot receives

Miss one and the bot either doesn’t work or has bizarre partial functionality. Let’s set it up correctly from the start.

Step 1: Create Discord Application

Go to discord.com/developers/applications and log in. Click New Application, name it “OpenClaw Bot”, accept the ToS, and click Create. You now have a Discord Application — the container for your bot. Copy the Application ID from the General Information tab; you’ll need it later.

Step 2: Create Bot User & Token

  1. 1.Click Bot in the left sidebar
  2. 2.Click Add Bot, then confirm
  3. 3.Under Token, click Reset Token, then Copy immediately
  4. 4.Save it somewhere secure — you can’t view it again, only reset
? Tip:The bot token is like a password. Never commit it to git. Use environment variables.

Bot Settings

  • Public Bot — uncheck unless you want anyone to add it
  • Require OAuth2 Code Grant — leave unchecked

Step 2.5: Privileged Gateway Intents (CRITICAL)

This is where 80% of new bot devs fail. Still in the Bot tab, scroll to Privileged Gateway Intents and enable:

  • Presence Intent — see when users go online/offline
  • Server Members Intent — see member join/leave events
  • Message Content Intent — read message content

Without Message Content Intent, your bot can see that messages exist but can’t read what they say. It’s like having eyes but no brain. Discord made this privileged for privacy reasons, but OpenClaw needs it. Click Save Changes at the bottom.

Step 3: OAuth2 Scopes & Permissions

Click OAuth2 → URL Generator. Under SCOPES, check:

  • bot — your application has a bot user
  • applications.commands — bot can create slash commands

Minimal Bot Permissions

  • Read Messages / View Channels
  • Send Messages
  • Embed Links
  • Attach Files
  • Read Message History
  • Add Reactions

Additional Permissions for Moderation

  • Manage Messages — delete spam
  • Kick Members
  • Ban Members
  • Manage Roles
? Tip:Don’t go overboard. Only request permissions you actually need — users see the full list when adding the bot.

At the bottom, copy the generated invite URL. It looks like:

https://discord.com/api/oauth2/authorize?client_id=APP_ID&permissions=274878024768&scope=bot%20applications.commands

Step 4: Add Bot to Your Server

  1. 1.Paste the OAuth2 URL into your browser
  2. 2.Select which server (you need Manage Server permission)
  3. 3.Review permissions, click Authorize, complete CAPTCHA

The bot appears in your member list with a “BOT” tag (offline until you start the code).

Step 5: Configure OpenClaw for Discord

In your .env file:

DISCORD_ENABLED=true
DISCORD_BOT_TOKEN=your-bot-token-here
DISCORD_APPLICATION_ID=123456789012345678

Create the Discord Adapter

// adapters/discord.js
const { Client, GatewayIntentBits, Partials } = require('discord.js');

class DiscordAdapter {
  constructor(config) { this.config = config; this.client = null; }

  async initialize() {
    this.client = new Client({
      intents: [
        GatewayIntentBits.Guilds,
        GatewayIntentBits.GuildMessages,
        GatewayIntentBits.MessageContent,
        GatewayIntentBits.GuildMembers,
      ],
      partials: [Partials.Channel, Partials.Message],
    });

    this.client.on('ready', () => {
      console.log(`[Discord] Logged in as ${this.client.user.tag}`);
    });

    this.client.on('messageCreate', async (msg) => await this.handleMessage(msg));
    this.client.on('interactionCreate', async (i) => await this.handleInteraction(i));

    await this.client.login(this.config.token);
  }

  async handleMessage(message) {
    if (message.author.bot) return;
    const isMentioned = message.mentions.has(this.client.user);
    const isDM = message.channel.type === 'DM';
    if (!isMentioned && !isDM) return;
    const text = message.content.replace(`<@${this.client.user.id}>`, '').trim();
    const response = await this.processWithOpenClaw(message.author.id, text);
    await message.reply(response);
  }

  async handleInteraction(interaction) {
    if (!interaction.isChatInputCommand()) return;
    await interaction.deferReply();
    const response = await this.processSlashCommand(interaction);
    await interaction.editReply(response);
  }
}

module.exports = DiscordAdapter;

Install discord.js: npm install discord.js. Then enable the channel in config/channels.yml:

channels:
  discord:
    enabled: true
    adapter: adapters/discord.js
    token: ${DISCORD_BOT_TOKEN}
    application_id: ${DISCORD_APPLICATION_ID}
    respond_to_mentions: true
    respond_to_dms: true
    rate_limit:
      messages_per_second: 1

Step 6: Test Your Bot

Run npm start. You should see [Discord] Logged in as OpenClaw Bot#1234 and the bot will switch to Online (green) in your server. Mention it in any channel — @OpenClaw Bot what's 2+2? — or send a DM. If it doesn’t reply, check that the bot is online, that OpenClaw logs show the message arriving, and that Message Content Intent is enabled.

Step 7: Register Slash Commands

Slash commands are Discord’s modern command interface — they appear when users type /. Create scripts/register-discord-commands.js:

const { REST, Routes } = require('discord.js');
require('dotenv').config();

const commands = [
  {
    name: 'ask',
    description: 'Ask OpenClaw a question',
    options: [{ name: 'question', description: 'Your question', type: 3, required: true }],
  },
  { name: 'help', description: 'Show available commands' },
  { name: 'skills', description: 'List available skills' },
];

const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_BOT_TOKEN);

(async () => {
  await rest.put(
    Routes.applicationCommands(process.env.DISCORD_APPLICATION_ID),
    { body: commands }
  );
  console.log('Successfully registered slash commands!');
})();

Run node scripts/register-discord-commands.js. Global commands take up to 1 hour to appear; for instant testing, register against a single guild using Routes.applicationGuildCommands(APP_ID, GUILD_ID) instead.

Gateway Intents: What They Actually Mean

  • Guilds (always on) — required for the bot to work at all
  • Guild Members (privileged) — see members join/leave; required for welcome messages and role assignment
  • Guild Messages — know that messages were sent (but not their content)
  • Message Content (privileged) — actually read message text; the one most people forget
  • Guild Presences (privileged) — see online/offline/idle status

Permissions vs Intents (Common Confusion)

Permissions: what the bot can do in a server (send messages, ban members). Intents: what events the bot receives from Discord (messages, joins). A moderation bot needs the “Ban Members” permission AND the “Guild Members” intent. Missing either and the bot won’t work as expected.

Common Discord Bot Issues (And Fixes)

Bot doesn’t respond to messages

  • Cause 1: Message Content Intent disabled — re-enable in Developer Portal → Bot
  • Cause 2: Bot wasn’t mentioned and isn’t in a DM — adjust the trigger logic

Slash commands don’t appear

Re-run the registration script. Wait up to 1 hour for global commands, or use guild-scoped commands for instant updates.

Bot goes offline randomly

client.on('error', err => console.error('[Discord] Client error:', err));
process.on('unhandledRejection', err => console.error('[Discord] Unhandled:', err));

// Auto-restart with PM2
pm2 start npm --name "openclaw" -- start

Rate limit errors

Discord allows ~5 requests per 5 seconds per channel. Add a per-channel rate limiter and queue bursts.

Community Moderation Starter Persona

OpenClaw can act as a community moderator. A starter config/discord-moderation.yml:

moderation:
  enabled: true
  rules:
    spam_detection:
      enabled: true
      max_messages_per_minute: 10
      action: timeout
      duration: 300
    forbidden_words:
      enabled: true
      words: [word1, word2]
      action: delete_and_warn
    excessive_caps:
      enabled: true
      threshold: 0.7
      action: warn
    link_spam:
      enabled: true
      max_links_per_message: 3
      action: delete_and_timeout
  warnings:
    strike_limit: 3
    actions: { 1: warn_dm, 2: timeout_30min, 3: kick, 4: ban }
  exempt_roles: [Moderator, Admin]
  log_channel: mod-logs

Pair it with a moderator persona (firm but fair, transparent, never power-trippy) and OpenClaw will auto-delete spam, warn users, escalate to timeout/kick/ban, welcome new members, and log every action to a mod-logs channel.

Advanced Features

Embeds (Rich Messages)

const { EmbedBuilder } = require('discord.js');
const embed = new EmbedBuilder()
  .setColor('#FF8C2A')
  .setTitle('OpenClaw Response')
  .setDescription("Here's what I found:")
  .addFields(
    { name: 'Item 1', value: 'Description' },
    { name: 'Item 2', value: 'Description' },
  )
  .setTimestamp();
await message.reply({ embeds:  });

Buttons (Interactive UI)

const { ButtonBuilder, ActionRowBuilder, ButtonStyle } = require('discord.js');
const button = new ButtonBuilder()
  .setCustomId('confirm')
  .setLabel('Confirm')
  .setStyle(ButtonStyle.Primary);
const row = new ActionRowBuilder().addComponents(button);
await message.reply({ content: 'Confirm this action?', components: [row] });

Voice Channel Integration

Install @discordjs/voice for voice command bots, music bots, or audio announcements.

The PaioClaw Alternative

Self-hosted Discord setup runs ~30-45 minutes if everything goes smoothly, with ongoing maintenance for discord.js updates, command re-registration, rate limit tuning, and moderation rules. PaioClaw’s Discord integration is a 2-minute path: paste your bot token, pick a permissions template (basic or moderation), deploy. Pre-configured intents, slash commands auto-registered, moderation templates, multi-server support. Starts free, $4/month for paid plans.

DIY when: you need custom Discord-specific features, you’re building a Discord-focused product, or learning the API is the goal. Use PaioClaw when: you want Discord plus other channels (Slack, Telegram, WhatsApp), you don’t want to debug intents, or you need moderation features that just work.

The Bottom Line

Discord bot setup is more complex than Telegram but more structured than WhatsApp. The Developer Portal has a lot of options, but they’re all documented and logical. The critical thing: enable Message Content Intent. 80% of “my bot doesn’t work” issues trace back to this single checkbox.

Whether you self-host or use PaioClaw depends on whether you want to become a Discord API expert or just want your agent on Discord.

Join Our Community

Connect with other PaioClaw users, share tips, and stay up to date.