Skip to main content

Discord (Bot API)

Status: ready for DM and guild text channels via the official Discord bot gateway.

Goals

  • Talk to Clawdbot via Discord DMs or guild channels.
  • Direct chats collapse into the agent’s main session (default agent:main:main); guild channels stay isolated as agent:<agentId>:discord:channel:<channelId> (display names use discord:<guildSlug>#<channelSlug>).
  • Group DMs are ignored by default; enable via discord.dm.groupEnabled and optionally restrict by discord.dm.groupChannels.
  • Keep routing deterministic: replies always go back to the provider they arrived on.

How it works

  1. Create a Discord application → Bot, enable the intents you need (DMs + guild messages + message content), and grab the bot token.
  2. Invite the bot to your server with the permissions required to read/send messages where you want to use it.
  3. Configure Clawdbot with DISCORD_BOT_TOKEN (or discord.token in ~/.clawdbot/clawdbot.json).
  4. Run the gateway; it auto-starts the Discord provider only when a discord config section exists and the token is set (unless discord.enabled = false).
    • If you prefer env vars, still add discord: { enabled: true } to ~/.clawdbot/clawdbot.json and set DISCORD_BOT_TOKEN.
  5. Direct chats: use user:<id> (or a <@id> mention) when delivering; all turns land in the shared main session.
  6. Guild channels: use channel:<channelId> for delivery. Mentions are required by default and can be set per guild or per channel.
  7. Direct chats: secure by default via discord.dm.policy (default: "pairing"). Unknown senders get a pairing code (expires after 1 hour); approve via clawdbot pairing approve --provider discord <code>.
    • To keep old “open to anyone” behavior: set discord.dm.policy="open" and discord.dm.allowFrom=["*"].
    • To hard-allowlist: set discord.dm.policy="allowlist" and list senders in discord.dm.allowFrom.
    • To ignore all DMs: set discord.dm.enabled=false or discord.dm.policy="disabled".
  8. Group DMs are ignored by default; enable via discord.dm.groupEnabled and optionally restrict by discord.dm.groupChannels.
  9. Optional guild rules: set discord.guilds keyed by guild id (preferred) or slug, with per-channel rules.
  10. Optional native commands: set commands.native: true to register native commands in Discord; set commands.native: false to clear previously registered native commands. Text commands are controlled by commands.text and must be sent as standalone /... messages. Use commands.useAccessGroups: false to bypass access-group checks for commands.
  11. Optional guild context history: set discord.historyLimit (default 20) to include the last N guild messages as context when replying to a mention. Set 0 to disable.
  12. Reactions: the agent can trigger reactions via the discord tool (gated by discord.actions.*).
    • Reaction removal semantics: see /tools/reactions.
    • The discord tool is only exposed when the current provider is Discord.
  13. Native commands use isolated session keys (discord:slash:${userId}) rather than the shared main session.
Note: Discord does not provide a simple username → id lookup without extra guild context, so prefer ids or <@id> mentions for DM delivery targets. Note: Slugs are lowercase with spaces replaced by -. Channel names are slugged without the leading #. Note: Guild context [from:] lines include author.tag + id to make ping-ready replies easy.

How to create your own bot

This is the “Discord Developer Portal” setup for running Clawdbot in a server (guild) channel like #help.

1) Create the Discord app + bot user

  1. Discord Developer Portal → ApplicationsNew Application
  2. In your app:
    • BotAdd Bot
    • Copy the Bot Token (this is what you put in DISCORD_BOT_TOKEN)

2) Enable the gateway intents Clawdbot needs

Discord blocks “privileged intents” unless you explicitly enable them. In BotPrivileged Gateway Intents, enable:
  • Message Content Intent (required to read message text in most guilds; without it you’ll see “Used disallowed intents” or the bot will connect but not react to messages)
  • Server Members Intent (recommended; required for some member/user lookups and allowlist matching in guilds)
You usually do not need Presence Intent.

3) Generate an invite URL (OAuth2 URL Generator)

In your app: OAuth2URL Generator Scopes
  • bot
  • applications.commands (required for native commands)
Bot Permissions (minimal baseline)
  • ✅ View Channels
  • ✅ Send Messages
  • ✅ Read Message History
  • ✅ Embed Links
  • ✅ Attach Files
  • ✅ Add Reactions (optional but recommended)
  • ✅ Use External Emojis / Stickers (optional; only if you want them)
Avoid Administrator unless you’re debugging and fully trust the bot. Copy the generated URL, open it, pick your server, and install the bot.

4) Get the ids (guild/user/channel)

Discord uses numeric ids everywhere; Clawdbot config prefers ids.
  1. Discord (desktop/web) → User SettingsAdvanced → enable Developer Mode
  2. Right-click:
    • Server name → Copy Server ID (guild id)
    • Channel (e.g. #help) → Copy Channel ID
    • Your user → Copy User ID

5) Configure Clawdbot

Token

Set the bot token via env var (recommended on servers):
  • DISCORD_BOT_TOKEN=...
Or via config:
{
  discord: {
    enabled: true,
    token: "YOUR_BOT_TOKEN"
  }
}
Multi-account support: use discord.accounts with per-account tokens and optional name. See gateway/configuration for the shared pattern.

Allowlist + channel routing

Example “single server, only allow me, only allow #help”:
{
  discord: {
    enabled: true,
    dm: { enabled: false },
    guilds: {
      "YOUR_GUILD_ID": {
        users: ["YOUR_USER_ID"],
        requireMention: true,
        channels: {
          help: { allow: true, requireMention: true }
        }
      }
    },
    retry: {
      attempts: 3,
      minDelayMs: 500,
      maxDelayMs: 30000,
      jitter: 0.1
    }
  }
}
Notes:
  • requireMention: true means the bot only replies when mentioned (recommended for shared channels).
  • routing.groupChat.mentionPatterns also count as mentions for guild messages.
  • Multi-agent override: routing.agents.<agentId>.mentionPatterns takes precedence.
  • If channels is present, any channel not listed is denied by default.

6) Verify it works

  1. Start the gateway.
  2. In your server channel, send: @Krill hello (or whatever your bot name is).
  3. If nothing happens: check Troubleshooting below.

Troubleshooting

  • First: run clawdbot doctor and clawdbot providers status --probe (actionable warnings + quick audits).
  • “Used disallowed intents”: enable Message Content Intent (and likely Server Members Intent) in the Developer Portal, then restart the gateway.
  • Bot connects but never replies in a guild channel:
    • Missing Message Content Intent, or
    • The bot lacks channel permissions (View/Send/Read History), or
    • Your config requires mentions and you didn’t mention it, or
    • Your guild/channel allowlist denies the channel/user.
  • Permission audits (providers status --probe) only check numeric channel IDs. If you use slugs/names as discord.guilds.*.channels keys, the audit can’t verify permissions.
  • DMs don’t work: discord.dm.enabled=false, discord.dm.policy="disabled", or you haven’t been approved yet (discord.dm.policy="pairing").

Capabilities & limits

  • DMs and guild text channels (threads are treated as separate channels; voice not supported).
  • Typing indicators sent best-effort; message chunking uses discord.textChunkLimit (default 2000) and splits tall replies by line count (discord.maxLinesPerMessage, default 17).
  • File uploads supported up to the configured discord.mediaMaxMb (default 8 MB).
  • Mention-gated guild replies by default to avoid noisy bots.
  • Reply context is injected when a message references another message (quoted content + ids).
  • Native reply threading is off by default; enable with discord.replyToMode and reply tags.

Retry policy

Outbound Discord API calls retry on rate limits (429) using Discord retry_after when available, with exponential backoff and jitter. Configure via discord.retry. See Retry policy.

Config

{
  discord: {
    enabled: true,
    token: "abc.123",
    groupPolicy: "open",
    mediaMaxMb: 8,
    actions: {
      reactions: true,
      stickers: true,
      polls: true,
      permissions: true,
      messages: true,
      threads: true,
      pins: true,
      search: true,
      memberInfo: true,
      roleInfo: true,
      roles: false,
      channelInfo: true,
      voiceStatus: true,
      events: true,
      moderation: false
    },
    replyToMode: "off",
    dm: {
      enabled: true,
      policy: "pairing", // pairing | allowlist | open | disabled
      allowFrom: ["123456789012345678", "steipete"],
      groupEnabled: false,
      groupChannels: ["clawd-dm"]
    },
    guilds: {
      "*": { requireMention: true },
      "123456789012345678": {
        slug: "friends-of-clawd",
        requireMention: false,
        reactionNotifications: "own",
        users: ["987654321098765432", "steipete"],
        channels: {
          general: { allow: true },
          help: {
            allow: true,
            requireMention: true,
            users: ["987654321098765432"],
            skills: ["search", "docs"],
            systemPrompt: "Keep answers short."
          }
        }
      }
    }
  }
}
Ack reactions are controlled globally via messages.ackReaction + messages.ackReactionScope.
  • dm.enabled: set false to ignore all DMs (default true).
  • dm.policy: DM access control (pairing recommended). "open" requires dm.allowFrom=["*"].
  • dm.allowFrom: DM allowlist (user ids or names). Used by dm.policy="allowlist" and for dm.policy="open" validation.
  • dm.groupEnabled: enable group DMs (default false).
  • dm.groupChannels: optional allowlist for group DM channel ids or slugs.
  • groupPolicy: controls guild channel handling (open|disabled|allowlist); allowlist requires channel allowlists.
  • guilds: per-guild rules keyed by guild id (preferred) or slug.
  • guilds."*": default per-guild settings applied when no explicit entry exists.
  • guilds.<id>.slug: optional friendly slug used for display names.
  • guilds.<id>.users: optional per-guild user allowlist (ids or names).
  • guilds.<id>.channels.<channel>.allow: allow/deny the channel when groupPolicy="allowlist".
  • guilds.<id>.channels.<channel>.requireMention: mention gating for the channel.
  • guilds.<id>.channels.<channel>.users: optional per-channel user allowlist.
  • guilds.<id>.channels.<channel>.skills: skill filter (omit = all skills, empty = none).
  • guilds.<id>.channels.<channel>.systemPrompt: extra system prompt for the channel (combined with channel topic).
  • guilds.<id>.channels.<channel>.enabled: set false to disable the channel.
  • guilds.<id>.channels: channel rules (keys are channel slugs or ids).
  • guilds.<id>.requireMention: per-guild mention requirement (overridable per channel).
  • guilds.<id>.reactionNotifications: reaction system event mode (off, own, all, allowlist).
  • textChunkLimit: outbound text chunk size (chars). Default: 2000.
  • maxLinesPerMessage: soft max line count per message. Default: 17.
  • mediaMaxMb: clamp inbound media saved to disk.
  • historyLimit: number of recent guild messages to include as context when replying to a mention (default 20, 0 disables).
  • retry: retry policy for outbound Discord API calls (attempts, minDelayMs, maxDelayMs, jitter).
  • actions: per-action tool gates; omit to allow all (set false to disable).
    • reactions (covers react + read reactions)
    • stickers, polls, permissions, messages, threads, pins, search
    • memberInfo, roleInfo, channelInfo, voiceStatus, events
    • roles (role add/remove, default false)
    • moderation (timeout/kick/ban, default false)
Reaction notifications use guilds.<id>.reactionNotifications:
  • off: no reaction events.
  • own: reactions on the bot’s own messages (default).
  • all: all reactions on all messages.
  • allowlist: reactions from guilds.<id>.users on all messages (empty list disables).

Tool action defaults

Action groupDefaultNotes
reactionsenabledReact + list reactions + emojiList
stickersenabledSend stickers
pollsenabledCreate polls
permissionsenabledChannel permission snapshot
messagesenabledRead/send/edit/delete
threadsenabledCreate/list/reply
pinsenabledPin/unpin/list
searchenabledMessage search (preview feature)
memberInfoenabledMember info
roleInfoenabledRole list
channelInfoenabledChannel info + list
voiceStatusenabledVoice state lookup
eventsenabledList/create scheduled events
rolesdisabledRole add/remove
moderationdisabledTimeout/kick/ban
  • replyToMode: off (default), first, or all. Applies only when the model includes a reply tag.

Reply tags

To request a threaded reply, the model can include one tag in its output:
  • [[reply_to_current]] — reply to the triggering Discord message.
  • [[reply_to:<id>]] — reply to a specific message id from context/history. Current message ids are appended to prompts as [message_id: …]; history entries already include ids.
Behavior is controlled by discord.replyToMode:
  • off: ignore tags.
  • first: only the first outbound chunk/attachment is a reply.
  • all: every outbound chunk/attachment is a reply.
Allowlist matching notes:
  • allowFrom/users/groupChannels accept ids, names, tags, or mentions like <@id>.
  • Prefixes like discord:/user: (users) and channel: (group DMs) are supported.
  • Use * to allow any sender/channel.
  • When guilds.<id>.channels is present, channels not listed are denied by default.
Native command notes:
  • The registered commands mirror Clawdbot’s chat commands.
  • Native commands honor the same allowlists as DMs/guild messages (discord.dm.allowFrom, discord.guilds, per-channel rules).

Tool actions

The agent can call discord with actions like:
  • react / reactions (add or list reactions)
  • sticker, poll, permissions
  • readMessages, sendMessage, editMessage, deleteMessage
  • threadCreate, threadList, threadReply
  • pinMessage, unpinMessage, listPins
  • searchMessages, memberInfo, roleInfo, roleAdd, roleRemove, emojiList
  • channelInfo, channelList, voiceStatus, eventList, eventCreate
  • timeout, kick, ban
Discord message ids are surfaced in the injected context ([discord message id: …] and history lines) so the agent can target them. Emoji can be unicode (e.g., ) or custom emoji syntax like <:party_blob:1234567890>.

Safety & ops

  • Treat the bot token like a password; prefer the DISCORD_BOT_TOKEN env var on supervised hosts or lock down the config file permissions.
  • Only grant the bot permissions it needs (typically Read/Send Messages).
  • If the bot is stuck or rate limited, restart the gateway (clawdbot gateway --force) after confirming no other processes own the Discord session.