Messaging platforms
Discord
Status: ready for DMs and guild channels via the official Discord gateway.
Quick setup
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).
Step 1: Create a Discord application and bot
Go to the Discord Developer Portal and click New Application. Name it something like “OpenClaw”.Click Bot on the sidebar. Set the Username to whatever you call your OpenClaw agent.
Step 2: Enable privileged intents
Still on the Bot page, scroll down to Privileged Gateway Intents and enable:
- 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)
Step 3: Copy your bot token
Scroll back up on the Bot page and click Reset Token.
ℹ️ Despite the name, this generates your first token — nothing is being “reset.”
Copy the token and save it somewhere. This is your Bot Token and you will need it shortly.
Step 4: Generate an invite URL and add the bot to your server
Click OAuth2 on the sidebar. You’ll generate an invite URL with the right permissions to add the bot to your server.Scroll down to OAuth2 URL Generator and enable:
botapplications.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.
Step 5: Enable Developer Mode and collect your IDs
Back in the Discord app, you need to enable Developer Mode so you can copy internal IDs.
- Click User Settings (gear icon next to your avatar) → Advanced → toggle on Developer Mode
- Right-click your server icon in the sidebar → Copy Server ID
- 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.
Step 6: Allow DMs from server members
For pairing to work, Discord needs to allow your bot to DM you. Right-click your server icon → Privacy Settings → toggle on Direct Messages.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.
Step 7: Step 0: Set your bot token securely (do not send it in chat)
Your Discord bot token is a secret (like a password). Set it on the machine running OpenClaw before messaging your agent.
openclaw config set channels.discord.token '"YOUR_BOT_TOKEN"' --json
openclaw config set channels.discord.enabled true --json
openclaw gateway
If OpenClaw is already running as a background service, use openclaw gateway restart instead.
Step 8: Configure OpenClaw and pair
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>.”
{
channels: {
discord: {
enabled: true,
token: "YOUR_BOT_TOKEN",
},
},
}
Step 9: Approve first DM pairing
Wait until the gateway is running, then DM your bot in Discord. It will respond with a pairing code.
Send the pairing code to your agent on your existing channel:
“Approve this Discord pairing code: ``”
openclaw pairing list discord
openclaw pairing approve discord <CODE>
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_TOKENis only used for the default account.
Recommended: Set up a guild workspace
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.
Step 1: Add your server to the guild allowlist
This enables your agent to respond in any channel on your server, not just DMs.
“Add my Discord Server ID
<server_id>to the guild allowlist”
{
channels: {
discord: {
groupPolicy: "allowlist",
guilds: {
YOUR_SERVER_ID: {
requireMention: true,
users: ["YOUR_USER_ID"],
},
},
},
},
}
Step 2: Allow responses without @mention
By default, your agent only responds in guild channels when @mentioned. For a private server, you probably want it to respond to every message.
“Allow my agent to respond on this server without having to be @mentioned”
{
channels: {
discord: {
guilds: {
YOUR_SERVER_ID: {
requireMention: false,
},
},
},
},
}
Step 3: Plan for memory in guild channels
By default, long-term memory (MEMORY.md) only loads in DM sessions. Guild channels do not auto-load MEMORY.md.
“When I ask questions in Discord channels, use memory_search or memory_get if you need long-term context from MEMORY.md.”
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.
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.
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::discord:channel:). - Group DMs are ignored by default (
channels.discord.dm.groupEnabled=false). - Native slash commands run in isolated command sessions (
agent::discord:slash:), while still carryingCommandTargetSessionKeyto the routed conversation session.
Forum channels
Discord forum and media channels only accept thread posts. OpenClaw supports two ways to create them:
- Send a message to the forum parent (
channel:) to auto-create a thread. The thread title uses the first non-empty line of your message. - Use
openclaw message thread createto create a thread directly. Do not pass--message-idfor 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:).
Interactive components
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,file- Action rows allow up to 5 buttons or a single select menu
- Select types:
string,user,role,mentionable,channel
By 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:
fileblocks must point to an attachment reference (attachment://)- Provide the attachment via
media/path/filePath(single file); usemedia-galleryfor multiple files - Use
filenameto override the upload name when it should match the attachment reference
Modal forms:
- Add
components.modalwith up to 5 fields - Field types:
text,checkbox,radio,select,role-select,user-select - OpenClaw adds a trigger button automatically
Example:
{
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" },
],
},
],
},
},
}
Access control and routing
channels.discord.dmPolicy controls DM access (legacy: channels.discord.dm.policy):
pairing(default)allowlistopen(requireschannels.discord.allowFromto 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.allowFromapplies only to thedefaultaccount.- Named accounts inherit
channels.discord.allowFromwhen their ownallowFromis unset. - Named accounts do not inherit
channels.discord.accounts.default.allowFrom.
DM target format for delivery:
user:<@id>mention
Bare numeric IDs are ambiguous and rejected unless an explicit user/channel target kind is provided.
{
channels: {
discord: {
groupPolicy: "allowlist",
guilds: {
"123456789012345678": {
requireMention: true,
ignoreOtherMentions: true,
users: ["987654321098765432"],
roles: ["123456789012345678"],
channels: {
general: { allow: true },
help: { allow: true, requireMention: true },
},
},
},
},
},
}
Guild messages are mention-gated by default.Mention detection includes:
- explicit bot mention
- configured mention patterns (
agents.list[].groupChat.mentionPatterns, fallbackmessages.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)
Role-based agent routing
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",
},
},
],
}
Developer Portal setup
- Discord Developer Portal -> Applications -> New Application
- Bot -> Add Bot
- Copy bot token
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.
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.
Enable Discord Developer Mode, then copy:
- server ID
- channel ID
- user ID
Prefer numeric IDs in OpenClaw config for reliable audits and probes.
Native commands and command auth
commands.nativedefaults to"auto"and is enabled for Discord.- Per-channel override:
channels.discord.commands.native. commands.native=falseexplicitly 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 for command catalog and behavior. Default slash command settings:
ephemeral: true
Feature details
Discord supports reply tags in agent output:
[[reply_to_current]][[reply_to:]]
Controlled by channels.discord.replyToMode:
off(default)firstall
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.
OpenClaw can stream draft replies by sending a temporary message and editing it as text arrives.
channels.discord.streamingcontrols preview streaming (off|partial|block|progress, default:off).progressis accepted for cross-channel consistency and maps topartialon Discord.channels.discord.streamModeis a legacy alias and is auto-migrated.partialedits a single preview message as tokens arrive.blockemits draft-sized chunks (usedraftChunkto 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.
Guild history context:
channels.discord.historyLimitdefault20- fallback:
messages.groupChat.historyLimit 0disables
DM history controls:
channels.discord.dmHistoryLimitchannels.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).
Discord can bind a thread to a session target so follow-up messages in that thread keep routing to the same session (including subagent sessions).Commands:
/focusbind current/new thread to a subagent/session target/unfocusremove current thread binding/agentsshow 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.spawnSubagentSessionsmust be true to auto-create/bind threads forsessions_spawn({ thread: true }).spawnAcpSessionsmust be true to auto-create/bind threads for ACP (/acp spawn ... --thread ...orsessions_spawn({ runtime: "acp", thread: true })).- If thread bindings are disabled for an account,
/focusand related thread binding operations are unavailable.
See Sub-agents, ACP Agents, and Configuration Reference.
For stable “always-on” ACP workspaces, configure top-level typed ACP bindings targeting Discord conversations.Config path:
bindings[]withtype: "acp"andmatch.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:
- Thread messages can inherit the parent channel ACP binding.
- In a bound channel or thread,
/newand/resetreset the same ACP session in place. - Temporary thread bindings still work and can override target resolution while active.
See ACP Agents for binding behavior details.
Per-guild reaction notification mode:
offown(default)allallowlist(usesguilds..users)
Reaction events are turned into system events and attached to the routed Discord session.
ackReaction sends an acknowledgement emoji while OpenClaw is processing an inbound message.Resolution order:
channels.discord.accounts..ackReactionchannels.discord.ackReactionmessages.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.
Channel-initiated config writes are enabled by default.This affects /config set|unset flows (when command features are enabled).Disable:
{
channels: {
discord: {
configWrites: false,
},
},
}
Route Discord gateway WebSocket traffic and startup REST lookups (application ID + allowlist resolution) through an HTTP(S) proxy with channels.discord.proxy.
{
channels: {
discord: {
proxy: "http://proxy.example:8080",
},
},
}
Per-account override:
{
channels: {
discord: {
accounts: {
primary: {
proxy: "http://proxy.example:8080",
},
},
},
},
}
Enable PluralKit resolution to map proxied messages to system member identity:
{
channels: {
discord: {
pluralkit: {
enabled: true,
token: "pk_live_...", // optional; needed for private systems
},
},
},
}
Notes:
- allowlists can use
pk: - 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
Presence updates are applied when you set a status or activity field, or when you enable auto presence.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.healthyTextautoPresence.degradedTextautoPresence.exhaustedText(supports{reason}placeholder)
Discord supports button-based exec approvals in DMs and can optionally post approval prompts in the originating channel.Config path:
channels.discord.execApprovals.enabledchannels.discord.execApprovals.approverschannels.discord.execApprovals.target(dm|channel|both, default:dm)agentFilter,sessionFilter,cleanupAfterResolve
When target is channel or both, the approval prompt is visible in the channel. Only configured 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.If approvals fail with unknown approval IDs, verify approver list and feature enablement.Related docs: Exec approvals
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 |
Components v2 UI
OpenClaw uses Discord components v2 for exec approvals and cross-context markers. Discord message actions can also accept components for custom UI (advanced; requires Carbon component instances), while legacy embeds remain available but are not recommended.
channels.discord.ui.components.accentColorsets the accent color used by Discord component containers (hex).- Set per account with
channels.discord.accounts..ui.components.accentColor. embedsare ignored when components v2 are present.
Example:
{
channels: {
discord: {
ui: {
components: {
accentColor: "#5865F2",
},
},
},
},
}
Voice channels
OpenClaw can join Discord voice channels for realtime, continuous conversations. This is separate from voice message attachments. Requirements:
- Enable native commands (
commands.nativeorchannels.discord.commands.native). - Configure
channels.discord.voice. - The bot needs Connect + Speak permissions in the target voice channel.
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.ttsoverridesmessages.ttsfor voice playback only.- Voice transcript turns derive owner status from Discord
allowFrom(ordm.allowFrom); non-owner speakers cannot access owner-only tools (for examplegatewayandcron). - Voice is enabled by default; set
channels.discord.voice.enabled=falseto disable it. voice.daveEncryptionandvoice.decryptionFailureTolerancepass through to@discordjs/voicejoin options.@discordjs/voicedefaults aredaveEncryption=trueanddecryptionFailureTolerance=24if unset.- OpenClaw also watches receive decrypt failures and auto-recovers by leaving/rejoining the voice channel after repeated failures in a short window.
- If receive logs repeatedly show
DecryptionFailed(UnencryptedWhenPassthroughDisabled), this may be the upstream@discordjs/voicereceive bug tracked in discord.js #11419.
Voice messages
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:
- Provide a local file path (URLs are rejected).
- Omit text content (Discord does not allow text + voice message in the same payload).
- Any audio format is accepted; OpenClaw converts to OGG/Opus when needed.
Example:
message(action="send", channel="discord", target="channel:123", path="/path/to/audio.mp3", asVoice=true)
Troubleshooting
-
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
channelsmap exists, only listed channels are allowed -
verify
requireMentionbehavior and mention patterns
Useful checks:
openclaw doctor
openclaw channels status --probe
openclaw logs --follow
Common causes:
groupPolicy="allowlist"without matching guild/channel allowlistrequireMentionconfigured in the wrong place (must be underchannels.discord.guildsor channel entry)- sender blocked by guild/channel
usersallowlist
Typical logs:
Listener DiscordMessageListener timed out after 30000ms for event MESSAGE_CREATESlow listener detected ...discord inbound worker timed out after ...
Listener budget knob:
- single-account:
channels.discord.eventQueue.listenerTimeout - multi-account:
channels.discord.accounts..eventQueue.listenerTimeout
Worker run timeout knob:
- single-account:
channels.discord.inboundWorker.runTimeoutMs - multi-account:
channels.discord.accounts..inboundWorker.runTimeoutMs - default:
1800000(30 minutes); set0to 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.
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.
- DM disabled:
channels.discord.dm.enabled=false - DM policy disabled:
channels.discord.dmPolicy="disabled"(legacy:channels.discord.dm.policy) - awaiting pairing approval in
pairingmode
By default bot-authored messages are ignored.If 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 detecteddiscord voice: repeated decrypt failures; attempting rejoin
- if failures continue after automatic rejoin, collect logs and compare against discord.js #11419
Configuration reference pointers
Primary reference:
High-signal Discord fields:
- startup/auth:
enabled,token,accounts.*,allowBots - policy:
groupPolicy,dm.*,guilds.*,guilds.*.channels.* - command:
commands.native,commands.useAccessGroups,configWrites,slashCommand.* - event queue:
eventQueue.listenerTimeout(listener budget),eventQueue.maxQueueSize,eventQueue.maxConcurrency - inbound worker:
inboundWorker.runTimeoutMs - reply/history:
replyToMode,historyLimit,dmHistoryLimit,dms.*.historyLimit - delivery:
textChunkLimit,chunkMode,maxLinesPerMessage - streaming:
streaming(legacy alias:streamMode),draftChunk,blockStreaming,blockStreamingCoalesce - media/retry:
mediaMaxMb,retrymediaMaxMbcaps outbound Discord uploads (default:8MB)
- actions:
actions.* - presence:
activity,status,activityType,activityUrl - UI:
ui.components.accentColor - features:
threadBindings, top-levelbindings[](type: "acp"),pluralkit,execApprovals,intents,agentComponents,heartbeat,responsePrefix
Safety and operations
- Treat bot tokens as secrets (
DISCORD_BOT_TOKENpreferred in supervised environments). - Grant least-privilege Discord permissions.
- If command deploy/state is stale, restart gateway and re-check with
openclaw channels status --probe.