Files
openclaw/docs/channels/discord.md

457 lines
13 KiB
Markdown

---
summary: "Discord bot support status, capabilities, and configuration"
read_when:
- Working on Discord channel features
title: "Discord"
---
# Discord (Bot API)
Status: ready for DMs and guild channels via the official Discord gateway.
<CardGroup cols={3}>
<Card title="Pairing" icon="link" href="/channels/pairing">
Discord DMs default to pairing mode.
</Card>
<Card title="Slash commands" icon="terminal" href="/tools/slash-commands">
Native command behavior and command catalog.
</Card>
<Card title="Channel troubleshooting" icon="wrench" href="/channels/troubleshooting">
Cross-channel diagnostics and repair flow.
</Card>
</CardGroup>
## Quick setup
<Steps>
<Step title="Create a Discord bot and enable intents">
Create an application in the Discord Developer Portal, add a bot, then enable:
- **Message Content Intent**
- **Server Members Intent** (recommended for name-to-ID lookups and allowlist matching)
</Step>
<Step title="Configure token">
```json5
{
channels: {
discord: {
enabled: true,
token: "YOUR_BOT_TOKEN",
},
},
}
```
Env fallback for the default account:
```bash
DISCORD_BOT_TOKEN=...
```
</Step>
<Step title="Invite the bot and start gateway">
Invite the bot to your server with message permissions.
```bash
openclaw gateway
```
</Step>
<Step title="Approve first DM pairing">
```bash
openclaw pairing list discord
openclaw pairing approve discord <CODE>
```
Pairing codes expire after 1 hour.
</Step>
</Steps>
<Note>
Token resolution is account-aware. Config token values win over env fallback. `DISCORD_BOT_TOKEN` is only used for the default account.
</Note>
## Runtime model
- Gateway owns the Discord connection.
- Reply routing is deterministic: Discord inbound replies back to Discord.
- By default (`session.dmScope=main`), direct chats share the agent main session (`agent:main:main`).
- Guild channels are isolated session keys (`agent:<agentId>:discord:channel:<channelId>`).
- Group DMs are ignored by default (`channels.discord.dm.groupEnabled=false`).
- Native slash commands run in isolated command sessions (`agent:<agentId>:discord:slash:<userId>`), while still carrying `CommandTargetSessionKey` to the routed conversation session.
## Access control and routing
<Tabs>
<Tab title="DM policy">
`channels.discord.dm.policy` controls DM access:
- `pairing` (default)
- `allowlist`
- `open` (requires `channels.discord.dm.allowFrom` to include `"*"`)
- `disabled`
If DM policy is not open, unknown users are blocked (or prompted for pairing in `pairing` mode).
DM target format for delivery:
- `user:<id>`
- `<@id>` mention
Bare numeric IDs are ambiguous and rejected unless an explicit user/channel target kind is provided.
</Tab>
<Tab title="Guild policy">
Guild handling is controlled by `channels.discord.groupPolicy`:
- `open`
- `allowlist`
- `disabled`
Secure baseline when `channels.discord` exists is `allowlist`.
`allowlist` behavior:
- guild must match `channels.discord.guilds` (`id` preferred, slug accepted)
- if a guild has `channels` configured, non-listed channels are denied
- if a guild has no `channels` block, all channels in that allowlisted guild are allowed
Example:
```json5
{
channels: {
discord: {
groupPolicy: "allowlist",
guilds: {
"123456789012345678": {
requireMention: true,
users: ["987654321098765432"],
channels: {
general: { allow: true },
help: { allow: true, requireMention: true },
},
},
},
},
},
}
```
If you only set `DISCORD_BOT_TOKEN` and do not create a `channels.discord` block, runtime fallback is `groupPolicy="open"` (with a warning in logs).
</Tab>
<Tab title="Mentions and group DMs">
Guild messages are mention-gated by default.
Mention detection includes:
- explicit bot mention
- configured mention patterns (`agents.list[].groupChat.mentionPatterns`, fallback `messages.groupChat.mentionPatterns`)
- implicit reply-to-bot behavior in supported cases
`requireMention` is configured per guild/channel (`channels.discord.guilds...`).
Group DMs:
- default: ignored (`dm.groupEnabled=false`)
- optional allowlist via `dm.groupChannels` (channel IDs or slugs)
</Tab>
</Tabs>
## Developer Portal setup
<AccordionGroup>
<Accordion title="Create app and bot">
1. Discord Developer Portal -> **Applications** -> **New Application**
2. **Bot** -> **Add Bot**
3. Copy bot token
</Accordion>
<Accordion title="Privileged intents">
In **Bot -> Privileged Gateway Intents**, enable:
- Message Content Intent
- Server Members Intent (recommended)
Presence intent is optional and only required if you want to receive presence updates. Setting bot presence (`setPresence`) does not require enabling presence updates for members.
</Accordion>
<Accordion title="OAuth scopes and baseline permissions">
OAuth URL generator:
- scopes: `bot`, `applications.commands`
Typical baseline permissions:
- View Channels
- Send Messages
- Read Message History
- Embed Links
- Attach Files
- Add Reactions (optional)
Avoid `Administrator` unless explicitly needed.
</Accordion>
<Accordion title="Copy IDs">
Enable Discord Developer Mode, then copy:
- server ID
- channel ID
- user ID
Prefer numeric IDs in OpenClaw config for reliable audits and probes.
</Accordion>
</AccordionGroup>
## Native commands and command auth
- `commands.native` defaults to `"auto"` and is enabled for Discord.
- Per-channel override: `channels.discord.commands.native`.
- `commands.native=false` explicitly clears previously registered Discord native commands.
- Native command auth uses the same Discord allowlists/policies as normal message handling.
- Commands may still be visible in Discord UI for users who are not authorized; execution still enforces OpenClaw auth and returns "not authorized".
See [Slash commands](/tools/slash-commands) for command catalog and behavior.
## Feature details
<AccordionGroup>
<Accordion title="Reply tags and native replies">
Discord supports reply tags in agent output:
- `[[reply_to_current]]`
- `[[reply_to:<id>]]`
Controlled by `channels.discord.replyToMode`:
- `off` (default)
- `first`
- `all`
Message IDs are surfaced in context/history so agents can target specific messages.
</Accordion>
<Accordion title="History, context, and thread behavior">
Guild history context:
- `channels.discord.historyLimit` default `20`
- fallback: `messages.groupChat.historyLimit`
- `0` disables
DM history controls:
- `channels.discord.dmHistoryLimit`
- `channels.discord.dms["<user_id>"].historyLimit`
Thread behavior:
- Discord threads are routed as channel sessions
- parent thread metadata can be used for parent-session linkage
- thread config inherits parent channel config unless a thread-specific entry exists
Channel topics are injected as **untrusted** context (not as system prompt).
</Accordion>
<Accordion title="Reaction notifications">
Per-guild reaction notification mode:
- `off`
- `own` (default)
- `all`
- `allowlist` (uses `guilds.<id>.users`)
Reaction events are turned into system events and attached to the routed Discord session.
</Accordion>
<Accordion title="Config writes">
Channel-initiated config writes are enabled by default.
This affects `/config set|unset` flows (when command features are enabled).
Disable:
```json5
{
channels: {
discord: {
configWrites: false,
},
},
}
```
</Accordion>
<Accordion title="PluralKit support">
Enable PluralKit resolution to map proxied messages to system member identity:
```json5
{
channels: {
discord: {
pluralkit: {
enabled: true,
token: "pk_live_...", // optional; needed for private systems
},
},
},
}
```
Notes:
- allowlists can use `pk:<memberId>`
- member display names are matched by name/slug
- lookups use original message ID and are time-window constrained
- if lookup fails, proxied messages are treated as bot messages and dropped unless `allowBots=true`
</Accordion>
<Accordion title="Exec approvals in Discord">
Discord supports button-based exec approvals in DMs.
Config path:
- `channels.discord.execApprovals.enabled`
- `channels.discord.execApprovals.approvers`
- `agentFilter`, `sessionFilter`, `cleanupAfterResolve`
If approvals fail with unknown approval IDs, verify approver list and feature enablement.
Related docs: [Exec approvals](/tools/exec-approvals)
</Accordion>
</AccordionGroup>
## Tools and action gates
Discord message actions include messaging, channel admin, moderation, presence, and metadata actions.
Core examples:
- messaging: `sendMessage`, `readMessages`, `editMessage`, `deleteMessage`, `threadReply`
- reactions: `react`, `reactions`, `emojiList`
- moderation: `timeout`, `kick`, `ban`
- presence: `setPresence`
Action gates live under `channels.discord.actions.*`.
Default gate behavior:
| Action group | Default |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- |
| reactions, messages, threads, pins, polls, search, memberInfo, roleInfo, channelInfo, channels, voiceStatus, events, stickers, emojiUploads, stickerUploads, permissions | enabled |
| roles | disabled |
| moderation | disabled |
| presence | disabled |
## Troubleshooting
<AccordionGroup>
<Accordion title="Used disallowed intents or bot sees no guild messages">
- enable Message Content Intent
- enable Server Members Intent when you depend on user/member resolution
- restart gateway after changing intents
</Accordion>
<Accordion title="Guild messages blocked unexpectedly">
- verify `groupPolicy`
- verify guild allowlist under `channels.discord.guilds`
- if guild `channels` map exists, only listed channels are allowed
- verify `requireMention` behavior and mention patterns
Useful checks:
```bash
openclaw doctor
openclaw channels status --probe
openclaw logs --follow
```
</Accordion>
<Accordion title="Require mention false but still blocked">
Common causes:
- `groupPolicy="allowlist"` without matching guild/channel allowlist
- `requireMention` configured in the wrong place (must be under `channels.discord.guilds` or channel entry)
- sender blocked by guild/channel `users` allowlist
</Accordion>
<Accordion title="Permissions audit mismatches">
`channels status --probe` permission checks only work for numeric channel IDs.
If you use slug keys, runtime matching can still work, but probe cannot fully verify permissions.
</Accordion>
<Accordion title="DM and pairing issues">
- DM disabled: `channels.discord.dm.enabled=false`
- DM policy disabled: `channels.discord.dm.policy="disabled"`
- awaiting pairing approval in `pairing` mode
</Accordion>
<Accordion title="Bot to bot loops">
By default bot-authored messages are ignored.
If you set `channels.discord.allowBots=true`, use strict mention and allowlist rules to avoid loop behavior.
</Accordion>
</AccordionGroup>
## Configuration reference pointers
Primary reference:
- [Configuration reference - Discord](/gateway/configuration-reference#discord)
High-signal Discord fields:
- startup/auth: `enabled`, `token`, `accounts.*`, `allowBots`
- policy: `groupPolicy`, `dm.*`, `guilds.*`, `guilds.*.channels.*`
- command: `commands.native`, `commands.useAccessGroups`, `configWrites`
- reply/history: `replyToMode`, `historyLimit`, `dmHistoryLimit`, `dms.*.historyLimit`
- delivery: `textChunkLimit`, `chunkMode`, `maxLinesPerMessage`
- media/retry: `mediaMaxMb`, `retry`
- actions: `actions.*`
- features: `pluralkit`, `execApprovals`, `intents`, `agentComponents`, `heartbeat`, `responsePrefix`
## Safety and operations
- Treat bot tokens as secrets (`DISCORD_BOT_TOKEN` preferred in supervised environments).
- Grant least-privilege Discord permissions.
- If command deploy/state is stale, restart gateway and re-check with `openclaw channels status --probe`.
## Related
- [Pairing](/channels/pairing)
- [Channel routing](/channels/channel-routing)
- [Troubleshooting](/channels/troubleshooting)
- [Slash commands](/tools/slash-commands)