Pairing
Pairing — Discord DMs default to pairing mode.
Status: ready for DMs and guild channels via the official Discord gateway.
Pairing
Pairing — Discord DMs default to pairing mode.
Slash commands
Slash commands — Native command behavior and command catalog.
Channel troubleshooting
Channel troubleshooting — Cross-channel diagnostics and repair flow.
You will need to create a new application with a bot, add the bot to your server, and pair it to RemoteClaw. 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).
Create a Discord application and bot
Go to the Discord Developer Portal and click New Application. Name it something like “RemoteClaw”.
Click Bot on the sidebar. Set the Username to whatever you call your RemoteClaw agent.
Enable privileged intents
Still on the Bot page, scroll down to Privileged Gateway Intents and enable:
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.
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.commandsA Bot Permissions section will appear below. Enable:
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.
Enable Developer Mode and collect your IDs
Back in the Discord app, you need to enable Developer Mode so you can copy internal IDs.
Save your Server ID and User ID alongside your Bot Token — you’ll send all three to RemoteClaw in the next step.
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 RemoteClaw. If you only plan to use guild channels, you can disable DMs after pairing.
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 RemoteClaw before messaging your agent.
remoteclaw config set channels.discord.token '"YOUR_BOT_TOKEN"' --jsonremoteclaw config set channels.discord.enabled true --jsonremoteclaw gatewayIf RemoteClaw is already running as a background service, use remoteclaw gateway restart instead.
Configure RemoteClaw and pair
Chat with your RemoteClaw 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>.”
If you prefer file-based config, set:
{ channels: { discord: { enabled: true, token: "YOUR_BOT_TOKEN", }, },}Env fallback for the default account:
DISCORD_BOT_TOKEN=...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:
<CODE>”
remoteclaw pairing list discordremoteclaw 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_TOKEN is only used for the default account.
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.
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"], }, }, }, },}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”
Set requireMention: false in your guild config:
{ channels: { discord: { guilds: { YOUR_SERVER_ID: { requireMention: false, }, }, }, },}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.
If you need shared context in every channel, put the stable instructions in native agent config (e.g. CLAUDE.md for Claude Code). Keep long-term notes in MEMORY.md.
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. RemoteClaw 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.remoteclaw 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
remoteclaw message send --channel discord --target channel:<forumId> \ --message "Topic title\nBody of the post"Example: create a forum thread explicitly
remoteclaw 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>).
RemoteClaw 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" }, ], }, ], }, },}channels.discord.dmPolicy controls DM access (legacy: channels.discord.dm.policy):
pairing (default)allowlistopen (requires channels.discord.allowFrom to include "*"; legacy: channels.discord.dm.allowFrom)disabledIf DM policy is not open, unknown users are blocked (or prompted for pairing in pairing mode).
DM target format for delivery:
user:<id><@id> mentionBare numeric IDs are ambiguous and rejected unless an explicit user/channel target kind is provided.
Guild handling is controlled by channels.discord.groupPolicy:
openallowlistdisabledSecure baseline when channels.discord exists is allowlist.
allowlist behavior:
channels.discord.guilds (id preferred, slug accepted)users (stable IDs recommended) and roles (role IDs only); if either is configured, senders are allowed when they match users OR roleschannels.discord.dangerouslyAllowNameMatching: true only as break-glass compatibility modeusers, but IDs are safer; remoteclaw security audit warns when name/tag entries are usedchannels configured, non-listed channels are deniedchannels block, all channels in that allowlisted guild are allowedExample:
{ channels: { discord: { groupPolicy: "allowlist", guilds: { "123456789012345678": { requireMention: 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.
Guild messages are mention-gated by default.
Mention detection includes:
agents.list[].groupChat.mentionPatterns, fallback messages.groupChat.mentionPatterns)requireMention is configured per guild/channel (channels.discord.guilds...).
Group DMs:
dm.groupEnabled=false)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", }, }, ],}In Bot -> Privileged Gateway Intents, enable:
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:
bot, applications.commandsTypical baseline permissions:
Avoid Administrator unless explicitly needed.
Enable Discord Developer Mode, then copy:
Prefer numeric IDs in RemoteClaw 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: trueDiscord supports reply tags in agent output:
[[reply_to_current]][[reply_to:<id>]]Controlled by channels.discord.replyToMode:
off (default)firstallNote: 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.
RemoteClaw can stream draft replies by sending a temporary message and editing it as text arrives.
channels.discord.streaming controls preview streaming (off | partial | block | progress, default: off).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, RemoteClaw skips the preview stream to avoid double streaming.
Guild history context:
channels.discord.historyLimit default 20messages.groupChat.historyLimit0 disablesDM history controls:
channels.discord.dmHistoryLimitchannels.discord.dms["<user_id>"].historyLimitThread behavior:
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:
/focus <target> bind current/new thread to a subagent/session target/unfocus remove current thread binding/agents show active runs and binding state/session ttl <duration|off> inspect/update auto-unfocus TTL for focused bindingsConfig:
{ session: { threadBindings: { enabled: true, ttlHours: 24, }, }, channels: { discord: { threadBindings: { enabled: true, ttlHours: 24, 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 })./focus and related thread binding operations are unavailable.See Sub-agents and Configuration Reference.
Per-guild reaction notification mode:
offown (default)allallowlist (uses guilds.<id>.users)Reaction events are turned into system events and attached to the routed Discord session.
ackReaction sends an acknowledgement emoji while RemoteClaw is processing an inbound message.
Resolution order:
channels.discord.accounts.<accountId>.ackReactionchannels.discord.ackReactionmessages.ackReactionagents.list[].identity.emoji, else ”👀”)Notes:
"" 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:
pk:<memberId>channels.discord.dangerouslyAllowNameMatching: trueallowBots=truePresence updates are applied only when you set a status or activity field.
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/remoteclaw", }, },}Activity type map:
activityUrl)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, cleanupAfterResolveWhen 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, RemoteClaw falls back to DM delivery.
If approvals fail with unknown approval IDs, verify approver list and feature enablement.
Related docs: 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 |
RemoteClaw 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.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", }, }, }, },}RemoteClaw 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.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. RemoteClaw 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)groupPolicychannels.discord.guildschannels map exists, only listed channels are allowedrequireMention behavior and mention patternsUseful checks:
remoteclaw doctorremoteclaw channels status --proberemoteclaw logs --followCommon causes:
groupPolicy="allowlist" without matching guild/channel allowlistrequireMention configured in the wrong place (must be under channels.discord.guilds or channel entry)users allowlistchannels 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.
channels.discord.dm.enabled=falsechannels.discord.dmPolicy="disabled" (legacy: channels.discord.dm.policy)pairing modeBy default bot-authored messages are ignored.
If you set channels.discord.allowBots=true, use strict mention and allowlist rules to avoid loop behavior.
remoteclaw update) so the Discord voice receive recovery logic is presentchannels.discord.voice.daveEncryption=true (default)channels.discord.voice.decryptionFailureTolerance=24 (upstream default) and tune only if neededdiscord voice: DAVE decrypt failures detecteddiscord voice: repeated decrypt failures; attempting rejoinPrimary reference:
High-signal Discord fields:
enabled, token, accounts.*, allowBotsgroupPolicy, dm.*, guilds.*, guilds.*.channels.*commands.native, commands.useAccessGroups, configWrites, slashCommand.*replyToMode, historyLimit, dmHistoryLimit, dms.*.historyLimittextChunkLimit, chunkMode, maxLinesPerMessagestreaming (legacy alias: streamMode), draftChunk, blockStreaming, blockStreamingCoalescemediaMaxMb, retryactions.*activity, status, activityType, activityUrlui.components.accentColorpluralkit, execApprovals, intents, agentComponents, heartbeat, responsePrefixDISCORD_BOT_TOKEN preferred in supervised environments).remoteclaw channels status --probe.