
OpenClaw Integration with Discord (Bot API)

By Sarah Jenkins


By Sarah Jenkins
Status: ready for DMs and guild channels via the official Discord gateway.
You will need to create a new application with a bot, add the bot to your server, and pair it to OpenClaw. We recommend adding your bot to your own private server. If you don't have one yet, create one first (choose Create My Own > For me and my friends).
Click **Bot** on the sidebar. Set the **Username** to whatever you call your OpenClaw agent.- **Message Content Intent** (required)
- **Server Members Intent** (recommended; required for role allowlists and name-to-ID matching)
- **Presence Intent** (optional; only needed for presence updates)<Note>
Despite the name, this generates your first token — nothing is being "reset."
</Note>
Copy the token and save it somewhere. This is your **Bot Token** and you will need it shortly.Scroll down to **OAuth2 URL Generator** and enable:
- `bot`
- `applications.commands`
A **Bot Permissions** section will appear below. Enable:
- View Channels
- Send Messages
- Read Message History
- Embed Links
- Attach Files
- Add Reactions (optional)
Copy the generated URL at the bottom, paste it into your browser, select your server, and click **Continue** to connect. You should now see your bot in the Discord server.1. Click **User Settings** (gear icon next to your avatar) → **Advanced** → toggle on **Developer Mode**
2. Right-click your **server icon** in the sidebar → **Copy Server ID**
3. Right-click your **own avatar** → **Copy User ID**
Save your **Server ID** and **User ID** alongside your Bot Token — you'll send all three to OpenClaw in the next step.This lets server members (including bots) send you DMs. Keep this enabled if you want to use Discord DMs with OpenClaw. If you only plan to use guild channels, you can disable DMs after pairing.export DISCORD_BOT_TOKEN="YOUR_BOT_TOKEN"
openclaw config set channels.discord.token --ref-provider default --ref-source env --ref-id DISCORD_BOT_TOKEN --dry-run
openclaw config set channels.discord.token --ref-provider default --ref-source env --ref-id DISCORD_BOT_TOKEN
openclaw config set channels.discord.enabled true --strict-json
openclaw gateway
If OpenClaw is already running as a background service, restart it via the OpenClaw Mac app or by stopping and restarting the `openclaw gateway run` process.<Tabs>
<Tab title="Ask your agent">
Chat with your OpenClaw agent on any existing channel (e.g. Telegram) and tell it. If Discord is your first channel, use the CLI / config tab instead.
> "I already set my Discord bot token in config. Please finish Discord setup with User ID `<user_id>` and Server ID `<server_id>`."
</Tab>
<Tab title="CLI / config">
If you prefer file-based config, set:{
channels: {
discord: {
enabled: true,
token: {
source: "env",
provider: "default",
id: "DISCORD_BOT_TOKEN",
},
},
},
}
Env fallback for the default account:DISCORD_BOT_TOKEN=...
Plaintext `token` values are supported. SecretRef values are also supported for `channels.discord.token` across env/file/exec providers. See [Secrets Management](/gateway/secrets).
</Tab>
</Tabs><Tabs>
<Tab title="Ask your agent">
Send the pairing code to your agent on your existing channel:
> "Approve this Discord pairing code: `<CODE>`"
</Tab>
<Tab title="CLI">openclaw pairing list discord
openclaw pairing approve discord <CODE>
</Tab>
</Tabs>
Pairing codes expire after 1 hour.
You should now be able to chat with your agent in Discord via DM.Token resolution is account-aware. Config token values win over env fallback. `DISCORD_BOT_TOKEN` is only used for the default account. For advanced outbound calls (message tool/channel actions), an explicit per-call `token` is used for that call. This applies to send and read/probe-style actions (for example read/search/fetch/thread/pins/permissions). Account policy/retry settings still come from the selected account in the active runtime snapshot.
Once DMs are working, you can set up your Discord server as a full workspace where each channel gets its own agent session with its own context. This is recommended for private servers where it's just you and your bot.
<Tabs>
<Tab title="Ask your agent">
> "Add my Discord Server ID `<server_id>` to the guild allowlist"
</Tab>
<Tab title="Config">{
channels: {
discord: {
groupPolicy: "allowlist",
guilds: {
YOUR_SERVER_ID: {
requireMention: true,
users: ["YOUR_USER_ID"],
},
},
},
},
}
</Tab>
</Tabs><Tabs>
<Tab title="Ask your agent">
> "Allow my agent to respond on this server without having to be @mentioned"
</Tab>
<Tab title="Config">
Set `requireMention: false` in your guild config:{
channels: {
discord: {
guilds: {
YOUR_SERVER_ID: {
requireMention: false,
},
},
},
},
}
</Tab>
</Tabs><Tabs>
<Tab title="Ask your agent">
> "When I ask questions in Discord channels, use memory_search or memory_get if you need long-term context from MEMORY.md."
</Tab>
<Tab title="Manual">
If you need shared context in every channel, put the stable instructions in `AGENTS.md` or `USER.md` (they are injected for every session). Keep long-term notes in `MEMORY.md` and access them on demand with memory tools.
</Tab>
</Tabs>Now create some channels on your Discord server and start chatting. Your agent can see the channel name, and each channel gets its own isolated session — so you can set up #coding, #home, #research, or whatever fits your workflow.
session.dmScope=main), direct chats share the agent main session (agent:main:main).agent:<agentId>:discord:channel:<channelId>).channels.discord.dm.groupEnabled=false).agent:<agentId>:discord:slash:<userId>), while still carrying CommandTargetSessionKey to the routed conversation session.Discord forum and media channels only accept thread posts. OpenClaw supports two ways to create them:
channel:<forumId>) to auto-create a thread. The thread title uses the first non-empty line of your message.openclaw message thread create to create a thread directly. Do not pass --message-id for forum channels.Example: send to forum parent to create a thread
openclaw message send --channel discord --target channel:<forumId> \
--message "Topic title\nBody of the post"
Example: create a forum thread explicitly
openclaw message thread create --channel discord --target channel:<forumId> \
--thread-name "Topic title" --message "Body of the post"
Forum parents do not accept Discord components. If you need components, send to the thread itself (channel:<threadId>).
OpenClaw supports Discord components v2 containers for agent messages. Use the message tool with a components payload. Interaction results are routed back to the agent as normal inbound messages and follow the existing Discord replyToMode settings.
Supported blocks:
text, section, separator, actions, media-gallery, filestring, user, role, mentionable, channelBy default, components are single use. Set components.reusable=true to allow buttons, selects, and forms to be used multiple times until they expire.
To restrict who can click a button, set allowedUsers on that button (Discord user IDs, tags, or *). When configured, unmatched users receive an ephemeral denial.
The /model and /models slash commands open an interactive model picker with provider and model dropdowns plus a Submit step. The picker reply is ephemeral and only the invoking user can use it.
File attachments:
file blocks must point to an attachment reference (attachment://<filename>)media/path/filePath (single file); use media-gallery for multiple filesfilename to override the upload name when it should match the attachment referenceModal forms:
components.modal with up to 5 fieldstext, checkbox, radio, select, role-select, user-selectExample:
{
channel: "discord",
action: "send",
to: "channel:123456789012345678",
message: "Optional fallback text",
components: {
reusable: true,
text: "Choose a path",
blocks: [
{
type: "actions",
buttons: [
{
label: "Approve",
style: "success",
allowedUsers: ["123456789012345678"],
},
{ label: "Decline", style: "danger" },
],
},
{
type: "actions",
select: {
type: "string",
placeholder: "Pick an option",
options: [
{ label: "Option A", value: "a" },
{ label: "Option B", value: "b" },
],
},
},
],
modal: {
title: "Details",
triggerLabel: "Open form",
fields: [
{ type: "text", label: "Requester" },
{
type: "select",
label: "Priority",
options: [
{ label: "Low", value: "low" },
{ label: "High", value: "high" },
],
},
],
},
},
}
DM policy`channels.discord.dmPolicy` controls DM access (legacy: `channels.discord.dm.policy`):
- `pairing` (default)
- `allowlist`
- `open` (requires `channels.discord.allowFrom` to include `"*"`; legacy: `channels.discord.dm.allowFrom`)
- `disabled`
If DM policy is not open, unknown users are blocked (or prompted for pairing in `pairing` mode).
Multi-account precedence:
- `channels.discord.accounts.default.allowFrom` applies only to the `default` account.
- Named accounts inherit `channels.discord.allowFrom` when their own `allowFrom` is unset.
- Named accounts do not inherit `channels.discord.accounts.default.allowFrom`.
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.Guild policyGuild 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)
- optional sender allowlists: `users` (stable IDs recommended) and `roles` (role IDs only); if either is configured, senders are allowed when they match `users` OR `roles`
- direct name/tag matching is disabled by default; enable `channels.discord.dangerouslyAllowNameMatching: true` only as break-glass compatibility mode
- names/tags are supported for `users`, but IDs are safer; `openclaw security audit` warns when name/tag entries are used
- 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:{
channels: {
discord: {
groupPolicy: "allowlist",
guilds: {
"123456789012345678": {
requireMention: true,
ignoreOtherMentions: true,
users: ["987654321098765432"],
roles: ["123456789012345678"],
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="allowlist"` (with a warning in logs), even if `channels.defaults.groupPolicy` is `open`.Mentions and group DMsGuild 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...`).
`ignoreOtherMentions` optionally drops messages that mention another user/role but not the bot (excluding @everyone/@here).
Group DMs:
- default: ignored (`dm.groupEnabled=false`)
- optional allowlist via `dm.groupChannels` (channel IDs or slugs)Use bindings[].match.roles to route Discord guild members to different agents by role ID. Role-based bindings accept role IDs only and are evaluated after peer or parent-peer bindings and before guild-only bindings. If a binding also sets other match fields (for example peer + guildId + roles), all configured fields must match.
{
bindings: [
{
agentId: "opus",
match: {
channel: "discord",
guildId: "123456789012345678",
roles: ["111111111111111111"],
},
},
{
agentId: "sonnet",
match: {
channel: "discord",
guildId: "123456789012345678",
},
},
],
}
1. Discord Developer Portal -> **Applications** -> **New Application**
2. **Bot** -> **Add Bot**
3. Copy bot token- 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.- 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.- server ID
- channel ID
- user ID
Prefer numeric IDs in OpenClaw config for reliable audits and probes.commands.native defaults to "auto" and is enabled for Discord.channels.discord.commands.native.commands.native=false explicitly clears previously registered Discord native commands.See Slash commands for command catalog and behavior.
Default slash command settings:
ephemeral: true- `[[reply_to_current]]`
- `[[reply_to:<id>]]`
Controlled by `channels.discord.replyToMode`:
- `off` (default)
- `first`
- `all`
Note: `off` disables implicit reply threading. Explicit `[[reply_to_*]]` tags are still honored.
Message IDs are surfaced in context/history so agents can target specific messages.- `channels.discord.streaming` controls preview streaming (`off` | `partial` | `block` | `progress`, default: `off`).
- Default stays `off` because Discord preview edits can hit rate limits quickly, especially when multiple bots or gateways share the same account or guild traffic.
- `progress` is accepted for cross-channel consistency and maps to `partial` on Discord.
- `channels.discord.streamMode` is a legacy alias and is auto-migrated.
- `partial` edits a single preview message as tokens arrive.
- `block` emits draft-sized chunks (use `draftChunk` to tune size and breakpoints).
Example:{
channels: {
discord: {
streaming: "partial",
},
},
}
`block` mode chunking defaults (clamped to `channels.discord.textChunkLimit`):{
channels: {
discord: {
streaming: "block",
draftChunk: {
minChars: 200,
maxChars: 800,
breakPreference: "paragraph",
},
},
},
}
Preview streaming is text-only; media replies fall back to normal delivery.
Note: preview streaming is separate from block streaming. When block streaming is explicitly
enabled for Discord, OpenClaw skips the preview stream to avoid double streaming.- `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).Commands:
- `/focus <target>` bind current/new thread to a subagent/session target
- `/unfocus` remove current thread binding
- `/agents` show active runs and binding state
- `/session idle <duration|off>` inspect/update inactivity auto-unfocus for focused bindings
- `/session max-age <duration|off>` inspect/update hard max age for focused bindings
Config:{
session: {
threadBindings: {
enabled: true,
idleHours: 24,
maxAgeHours: 0,
},
},
channels: {
discord: {
threadBindings: {
enabled: true,
idleHours: 24,
maxAgeHours: 0,
spawnSubagentSessions: false, // opt-in
},
},
},
}
Notes:
- `session.threadBindings.*` sets global defaults.
- `channels.discord.threadBindings.*` overrides Discord behavior.
- `spawnSubagentSessions` must be true to auto-create/bind threads for `sessions_spawn({ thread: true })`.
- `spawnAcpSessions` must be true to auto-create/bind threads for ACP (`/acp spawn ... --thread ...` or `sessions_spawn({ runtime: "acp", thread: true })`).
- If thread bindings are disabled for an account, `/focus` and related thread binding operations are unavailable.
See [Sub-agents](/tools/subagents), [ACP Agents](/tools/acp-agents), and [Configuration Reference](/gateway/configuration-reference).Config path:
- `bindings[]` with `type: "acp"` and `match.channel: "discord"`
Example:{
agents: {
list: [
{
id: "codex",
runtime: {
type: "acp",
acp: {
agent: "codex",
backend: "acpx",
mode: "persistent",
cwd: "/workspace/openclaw",
},
},
},
],
},
bindings: [
{
type: "acp",
agentId: "codex",
match: {
channel: "discord",
accountId: "default",
peer: { kind: "channel", id: "222222222222222222" },
},
acp: { label: "codex-main" },
},
],
channels: {
discord: {
guilds: {
"111111111111111111": {
channels: {
"222222222222222222": {
requireMention: false,
},
},
},
},
},
},
}
Notes:
- `/acp spawn codex --bind here` binds the current Discord channel or thread in place and keeps future messages routed to the same ACP session.
- That can still mean "start a fresh Codex ACP session", but it does not create a new Discord thread by itself. The existing channel stays the chat surface.
- Codex may still run in its own `cwd` or backend workspace on disk. That workspace is runtime state, not a Discord thread.
- Thread messages can inherit the parent channel ACP binding.
- In a bound channel or thread, `/new` and `/reset` reset the same ACP session in place.
- Temporary thread bindings still work and can override target resolution while active.
- `spawnAcpSessions` is only required when OpenClaw needs to create/bind a child thread via `--thread auto|here`. It is not required for `/acp spawn ... --bind here` in the current channel.
See [ACP Agents](/tools/acp-agents) for binding behavior details.- `off`
- `own` (default)
- `all`
- `allowlist` (uses `guilds.<id>.users`)
Reaction events are turned into system events and attached to the routed Discord session.Resolution order:
- `channels.discord.accounts.<accountId>.ackReaction`
- `channels.discord.ackReaction`
- `messages.ackReaction`
- agent identity emoji fallback (`agents.list[].identity.emoji`, else "👀")
Notes:
- Discord accepts unicode emoji or custom emoji names.
- Use `""` to disable the reaction for a channel or account.This affects `/config set|unset` flows (when command features are enabled).
Disable:{
channels: {
discord: {
configWrites: false,
},
},
}
{
channels: {
discord: {
proxy: "http://proxy.example:8080",
},
},
}
Per-account override:{
channels: {
discord: {
accounts: {
primary: {
proxy: "http://proxy.example:8080",
},
},
},
},
}
{
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 only when `channels.discord.dangerouslyAllowNameMatching: true`
- 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`Status only example:{
channels: {
discord: {
status: "idle",
},
},
}
Activity example (custom status is the default activity type):{
channels: {
discord: {
activity: "Focus time",
activityType: 4,
},
},
}
Streaming example:{
channels: {
discord: {
activity: "Live coding",
activityType: 1,
activityUrl: "https://twitch.tv/openclaw",
},
},
}
Activity type map:
- 0: Playing
- 1: Streaming (requires `activityUrl`)
- 2: Listening
- 3: Watching
- 4: Custom (uses the activity text as the status state; emoji is optional)
- 5: Competing
Auto presence example (runtime health signal):{
channels: {
discord: {
autoPresence: {
enabled: true,
intervalMs: 30000,
minUpdateIntervalMs: 15000,
exhaustedText: "token exhausted",
},
},
},
}
Auto presence maps runtime availability to Discord status: healthy => online, degraded or unknown => idle, exhausted or unavailable => dnd. Optional text overrides:
- `autoPresence.healthyText`
- `autoPresence.degradedText`
- `autoPresence.exhaustedText` (supports `{reason}` placeholder)Config path:
- `channels.discord.execApprovals.enabled`
- `channels.discord.execApprovals.approvers` (optional; falls back to owner IDs inferred from `allowFrom` and explicit DM `defaultTo` when possible)
- `channels.discord.execApprovals.target` (`dm` | `channel` | `both`, default: `dm`)
- `agentFilter`, `sessionFilter`, `cleanupAfterResolve`
Discord becomes an approval client when `enabled: true` and at least one approver can be resolved, either from `execApprovals.approvers` or from the account's existing owner config (`allowFrom`, legacy `dm.allowFrom`, or explicit DM `defaultTo`).
When `target` is `channel` or `both`, the approval prompt is visible in the channel. Only resolved approvers can use the buttons; other users receive an ephemeral denial. Approval prompts include the command text, so only enable channel delivery in trusted channels. If the channel ID cannot be derived from the session key, OpenClaw falls back to DM delivery.
Discord also renders the shared approval buttons used by other chat channels. The native Discord adapter mainly adds approver DM routing and channel fanout.
Gateway auth for this handler uses the same shared credential resolution contract as other Gateway clients:
- env-first local auth (`OPENCLAW_GATEWAY_TOKEN` / `OPENCLAW_GATEWAY_PASSWORD` then `gateway.auth.*`)
- in local mode, `gateway.remote.*` can be used as fallback only when `gateway.auth.*` is unset; configured-but-unresolved local SecretRefs fail closed
- remote-mode support via `gateway.remote.*` when applicable
- URL overrides are override-safe: CLI overrides do not reuse implicit credentials, and env overrides use env credentials only
Exec approvals expire after 30 minutes by default. If approvals fail with unknown approval IDs, verify approver resolution and feature enablement.
Related docs: [Exec approvals](/tools/exec-approvals)Discord message actions include messaging, channel admin, moderation, presence, and metadata actions.
Core examples:
sendMessage, readMessages, editMessage, deleteMessage, threadReplyreact, reactions, emojiListtimeout, kick, bansetPresenceAction 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 |
OpenClaw uses Discord components v2 for exec approvals and cross-context markers. Discord message actions can also accept components for custom UI (advanced; requires constructing a component payload via the discord tool), while legacy embeds remain available but are not recommended.
channels.discord.ui.components.accentColor sets the accent color used by Discord component containers (hex).channels.discord.accounts.<id>.ui.components.accentColor.embeds are ignored when components v2 are present.Example:
{
channels: {
discord: {
ui: {
components: {
accentColor: "#5865F2",
},
},
},
},
}
OpenClaw can join Discord voice channels for realtime, continuous conversations. This is separate from voice message attachments.
Requirements:
commands.native or channels.discord.commands.native).channels.discord.voice.Use the Discord-only native command /vc join|leave|status to control sessions. The command uses the account default agent and follows the same allowlist and group policy rules as other Discord commands.
Auto-join example:
{
channels: {
discord: {
voice: {
enabled: true,
autoJoin: [
{
guildId: "123456789012345678",
channelId: "234567890123456789",
},
],
daveEncryption: true,
decryptionFailureTolerance: 24,
tts: {
provider: "openai",
openai: { voice: "alloy" },
},
},
},
},
}
Notes:
voice.tts overrides messages.tts for voice playback only.allowFrom (or dm.allowFrom); non-owner speakers cannot access owner-only tools (for example gateway and cron).channels.discord.voice.enabled=false to disable it.voice.daveEncryption and voice.decryptionFailureTolerance pass through to @discordjs/voice join options.@discordjs/voice defaults are daveEncryption=true and decryptionFailureTolerance=24 if unset.DecryptionFailed(UnencryptedWhenPassthroughDisabled), this may be the upstream @discordjs/voice receive bug tracked in discord.js #11419.Discord voice messages show a waveform preview and require OGG/Opus audio plus metadata. OpenClaw generates the waveform automatically, but it needs ffmpeg and ffprobe available on the gateway host to inspect and convert audio files.
Requirements and constraints:
Example:
message(action="send", channel="discord", target="channel:123", path="/path/to/audio.mp3", asVoice=true)
- enable Message Content Intent
- enable Server Members Intent when you depend on user/member resolution
- restart gateway after changing intents- 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:openclaw doctor
openclaw channels status --probe
openclaw logs --follow
- `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` allowlistTypical logs:
- `Listener DiscordMessageListener timed out after 30000ms for event MESSAGE_CREATE`
- `Slow listener detected ...`
- `discord inbound worker timed out after ...`
Listener budget knob:
- single-account: `channels.discord.eventQueue.listenerTimeout`
- multi-account: `channels.discord.accounts.<accountId>.eventQueue.listenerTimeout`
Worker run timeout knob:
- single-account: `channels.discord.inboundWorker.runTimeoutMs`
- multi-account: `channels.discord.accounts.<accountId>.inboundWorker.runTimeoutMs`
- default: `1800000` (30 minutes); set `0` to disable
Recommended baseline:{
channels: {
discord: {
accounts: {
default: {
eventQueue: {
listenerTimeout: 120000,
},
inboundWorker: {
runTimeoutMs: 1800000,
},
},
},
},
},
}
Use `eventQueue.listenerTimeout` for slow listener setup and `inboundWorker.runTimeoutMs`
only if you want a separate safety valve for queued agent turns.If you use slug keys, runtime matching can still work, but probe cannot fully verify permissions.- DM disabled: `channels.discord.dm.enabled=false`
- DM policy disabled: `channels.discord.dmPolicy="disabled"` (legacy: `channels.discord.dm.policy`)
- awaiting pairing approval in `pairing` modeIf you set `channels.discord.allowBots=true`, use strict mention and allowlist rules to avoid loop behavior.
Prefer `channels.discord.allowBots="mentions"` to only accept bot messages that mention the bot.- keep OpenClaw current (`openclaw update`) so the Discord voice receive recovery logic is present
- confirm `channels.discord.voice.daveEncryption=true` (default)
- start from `channels.discord.voice.decryptionFailureTolerance=24` (upstream default) and tune only if needed
- watch logs for:
- `discord voice: DAVE decrypt failures detected`
- `discord voice: repeated decrypt failures; attempting rejoin`
- if failures continue after automatic rejoin, collect logs and compare against [discord.js #11419](https://github.com/discordjs/discord.js/issues/11419)High-signal Discord fields:
enabled, token, accounts.*, allowBotsgroupPolicy, dm.*, guilds.*, guilds.*.channels.*commands.native, commands.useAccessGroups, configWrites, slashCommand.*eventQueue.listenerTimeout (listener budget), eventQueue.maxQueueSize, eventQueue.maxConcurrencyinboundWorker.runTimeoutMsreplyToMode, historyLimit, dmHistoryLimit, dms.*.historyLimittextChunkLimit, chunkMode, maxLinesPerMessagestreaming (legacy alias: streamMode), draftChunk, blockStreaming, blockStreamingCoalescemediaMaxMb, retryactions.*activity, status, activityType, activityUrlui.components.accentColorthreadBindings, top-level bindings[] (type: "acp"), pluralkit, execApprovals, intents, agentComponents, heartbeat, responsePrefixDISCORD_BOT_TOKEN preferred in supervised environments).openclaw channels status --probe.About the author

Sarah Jenkins is a seasoned OpenClaw developer with a strong focus on optimizing high-performance computing solutions. Her work primarily involves crafting efficient parallel algorithms and enhancing GPU acceleration for complex scientific simulations. Jenkins is renowned for her meticulous attention to detail and her ability to translate intricate theoretical concepts into practical, robust OpenClaw implementations.

by Sarah Jenkins
by Sarah Jenkins