Voice
Discord has two distinct voice surfaces: realtime voice channels (continuous conversations) and voice message attachments (the waveform preview format). The gateway supports both.Voice channels
Setup checklist:- Enable Message Content Intent in the Discord Developer Portal.
- Enable Server Members Intent when role/user allowlists are used.
- Invite the bot with
botandapplications.commandsscopes. - Grant Connect, Speak, Send Messages, and Read Message History in the target voice channel.
- Enable native commands (
commands.nativeorchannels.discord.commands.native). - Configure
channels.discord.voice.
/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.
- The OpenAI
agent-proxyresponse and wake-name policies below require a GA realtime model, such asgpt-realtime-2.1. GPT-Live currently responds to audio autonomously and does not enforce those policies; do not rely on wake-name gating with GPT-Live in a shared voice channel. - Discord voice is opt-in for text-only configs; set
channels.discord.voice.enabled=true(or keep an existingchannels.discord.voiceblock) to enable/vccommands, the voice runtime, and theGuildVoiceStatesgateway intent.channels.discord.intents.voiceStatescan explicitly override the intent subscription; leave it unset to follow effective voice enablement. voice.modecontrols the conversation path. The default isagent-proxy: a realtime voice front end handles turn timing, interruption, and playback, delegates substantive work to the routed OpenClaw agent throughopenclaw_agent_consult, and treats the result like a typed Discord prompt from that speaker.stt-ttskeeps the older batch STT plus TTS flow.bidilets the realtime model converse directly while exposingopenclaw_agent_consultfor the OpenClaw brain.- Realtime voice keeps each speaker’s audio in a separate provider connection, so delayed transcripts and tool calls retain that speaker’s Discord identity. Everyone still uses the same routed OpenClaw agent conversation and one room playback queue. Direct
bidiconversation history belongs to each speaker’s realtime connection; use the OpenClaw agent consult for shared room history. Multiple speakers can consume more provider connections, and provider account limits still apply. The room retains at most eight speaker connections and reclaims idle connections once their captures, requests, and playback have finished. voice.agentSessioncontrols which OpenClaw conversation receives voice turns. Leave it unset for the voice channel’s own session, or set{ mode: "target", target: "channel:<text-channel-id>" }to make the voice channel act as the microphone/speaker extension of an existing Discord text channel session such as#maintainers.voice.modeloverrides the OpenClaw agent brain for Discord voice responses and realtime consults. Leave it unset to inherit the routed agent model. It is separate fromvoice.realtime.model.voice.followUserslets the bot join, move, and leave Discord voice with selected users. See Follow users in voice.agent-proxyroutes speech throughdiscord-voice, which preserves normal owner/tool authorization for the speaker and target session but hides the agentttstool because Discord voice owns playback. By default,agent-proxygives the consult full owner-equivalent tool access for owner speakers (voice.realtime.toolPolicy: "owner") and strongly prefers consulting the OpenClaw agent before substantive answers (voice.realtime.consultPolicy: "always"). In that defaultalwaysmode, the realtime layer does not auto-speak filler before the consult answer; it captures and transcribes speech, then speaks the routed OpenClaw answer. If multiple forced consult answers finish while Discord is still playing the first answer, later exact-speech answers are queued until playback idles instead of replacing speech mid-sentence.- Realtime voice buffers generated audio when Discord playback temporarily falls behind and tolerates brief provider or network gaps. Each provider response keeps its own buffered audio, including native tool continuations. Normal backpressure does not cancel the response, and queued answers wait until Discord finishes playing the previous answer, even if its provider response or audio encoder has already finished.
- OpenAI and xAI interruptions truncate each retained native audio item at the amount Discord consumed. Queued items are discarded at zero, and completed replies that have finished playing are left intact. Playback progress survives temporary gaps in the same response; OpenAI’s echo guard uses the combined consumed duration of retained items.
- If a speaker’s realtime connection fails, other speakers stay connected. Check the
realtime speaker failedlog and try speaking again to open a new connection. If the initial provider connection fails during/vc join, joining fails while an already-connected recorder keeps recording; check therealtime session failed terminallylog and retry/vc join. Temporary provider reconnects do not end the Discord voice session. - In
stt-ttsmode, STT usestools.media.audio;voice.modeldoes not affect transcription. stt-ttsreplies remain active until Discord finishes playing them; long responses are not cut off by a fixed one-minute playback deadline.- In realtime modes,
voice.realtime.provider,voice.realtime.model, andvoice.realtime.speakerVoiceconfigure the realtime audio session. For OpenAI Realtime 2.1 plus the Codex brain, usevoice.realtime.model: "gpt-realtime-2.1"andvoice.model: "openai/gpt-5.6-sol". - Realtime voice modes include small
IDENTITY.md,USER.md, andSOUL.mdprofile files in the realtime provider instructions by default so fast direct turns keep the same identity, user grounding, and persona as the routed OpenClaw agent. Setvoice.realtime.bootstrapContextFilesto a subset to customize this, or[]to disable it. Only those profile files are supported;AGENTS.mdstays in the normal agent context. The injected profile context does not replaceopenclaw_agent_consultfor workspace work, current facts, memory lookup, or tool-backed actions. - In OpenAI
agent-proxyrealtime mode, wake-name gating adapts to the room by default: one human can talk naturally without a wake name, while two or more humans must start or end a turn with one. Other bots do not count as people. Setvoice.realtime.requireWakeName: trueto always require a wake name orfalseto never require one. Configured wake names must be one or two words. Ifvoice.realtime.wakeNamesis unset, OpenClaw uses the routed agentnameplusOpenClaw, falling back to the agent id plusOpenClaw. An active wake-name gate disables realtime provider auto-response, routes accepted turns through the OpenClaw agent consult path, and gives a short spoken acknowledgement when an exact leading wake name is recognized from partial transcription before the final transcript arrives. Fuzzy name matching waits for the final transcript, so an unfinished ordinary word does not trigger an acknowledgement. The policy follows live joins and leaves without reconnecting voice. - The OpenAI realtime provider accepts current Realtime 2 event names and legacy Codex-compatible aliases for output audio and transcript events, so compatible provider snapshots can drift without dropping assistant audio.
voice.realtime.bargeIncontrols whether Discord speaker-start events interrupt active realtime playback. If unset, it follows the realtime provider’s input-audio interruption setting.voice.realtime.minBargeInAudioEndMscontrols the minimum assistant playback duration before an OpenAI realtime barge-in truncates audio. Default:250. Set0for immediate interruption in low-echo rooms, or raise it for echo-heavy speaker setups.voice.ttsoverridesttsforstt-ttsvoice playback only; realtime modes usevoice.realtime.speakerVoiceinstead. For an OpenAI voice on Discord playback, setvoice.tts.provider: "openai"and choose a Text-to-speech voice undervoice.tts.providers.openai.speakerVoice.cedaris a good masculine-sounding choice on the current OpenAI TTS model.- Per-channel Discord
systemPromptoverrides apply to voice transcript turns for that voice channel. - When OpenClaw joins a voice channel, the routed agent session receives a silent system event with the current participant roster. Later participant joins and leaves update that session without triggering an unsolicited spoken reply; Discord display names are treated as untrusted labels. Authorized voice turns also receive a fresh roster snapshot.
- Voice transcript turns and
/vccommands use Discord entries incommands.ownerAllowFromfor owner status. When no Discord command owner is configured, the selected Discord account’sallowFrom(or legacydm.allowFrom) can still authorize voice access without granting owner status. Agent tool visibility follows the configured tool policy for the routed session. - If
voice.autoJoinhas multiple entries for the same guild, OpenClaw joins the last configured channel for that guild. voice.autoJoin[].whenOccupieddefaults tofalse. Set it totruefor an auto-managed room that should contain the bot only while at least one human is present. OpenClaw joins on the first human arrival and leaves after the last human departs; the OpenClaw bot and other bots do not count. Startup, fresh gateway sessions, and resumed gateway sessions reconcile from Discord’s voice-state roster.- Occupancy management owns only sessions that it joined. A manual
/vc join, standalone transcript-only session, follow-user session, active session in another channel, or other ad-hoc join is not moved or disconnected when the configured room empties. Attaching transcript capture to an occupancy-managed session preserves that ownership. voice.allowedChannelsis an optional residency allowlist. Leave it unset to allow/vc joininto any authorized Discord voice channel. When set,/vc join, startup auto-join, and bot voice-state moves are restricted to the listed{ guildId, channelId }entries. Set it to an empty array to deny all Discord voice joins. If Discord moves the bot outside the allowlist, OpenClaw leaves that channel and rejoins the configured auto-join target when one is available.voice.daveEncryptionandvoice.decryptionFailureTolerancepass through to@discordjs/voicejoin options; the upstream defaults aredaveEncryption=trueanddecryptionFailureTolerance=24.- OpenClaw uses the bundled
libopus-wasmcodec for Discord voice receive and realtime raw PCM playback. It ships a pinned libopus WebAssembly build and does not require native opus addons. voice.connectTimeoutMscontrols the initial@discordjs/voiceReady wait for/vc joinand auto-join attempts. Default:30000.voice.reconnectGraceMscontrols how long OpenClaw waits for a disconnected voice session to begin reconnecting before destroying it. Default:15000.- In
stt-ttsmode, voice playback does not stop just because another user starts speaking. To avoid feedback loops, OpenClaw does not admit new conversational turns while TTS is playing; an explicitly started capture still records that speech. Speak after playback finishes for the next conversational turn. Realtime modes forward authorized speaker starts as barge-in signals when interruption is enabled. - In realtime modes, echo from speakers into an open mic can look like barge-in and interrupt playback. For echo-heavy Discord rooms, set
voice.realtime.providers.openai.interruptResponseOnInputAudio: falseto keep OpenAI from auto-interrupting on input audio. Addvoice.realtime.bargeIn: trueif you still want Discord speaker-start events to interrupt active playback. The OpenAI realtime bridge ignores playback truncations shorter thanvoice.realtime.minBargeInAudioEndMsas likely echo/noise and logs them as skipped instead of clearing Discord playback. voice.captureSilenceGraceMscontrols how long OpenClaw waits after Discord reports a speaker has stopped before finalizing that audio segment for STT. Default:2000; raise it if Discord splits normal pauses into choppy partial transcripts.- When ElevenLabs is the selected TTS provider, Discord voice playback uses streaming TTS and starts from the provider response stream. Providers without streaming support fall back to the synthesized temp-file path.
- OpenClaw 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)after updating, collect a dependency report and logs. The bundled@discordjs/voiceline includes the upstream padding fix from discord.js PR #11449, which closed discord.js issue #11419. The operation was abortedreceive events are expected when OpenClaw finalizes a captured speaker segment; they are verbose diagnostics, not warnings.- Verbose Discord voice logs include a bounded one-line STT transcript preview for each accepted speaker segment, so debugging shows both the user side and the agent reply side without dumping unbounded transcript text.
- In
agent-proxymode, forced consult fallback skips likely incomplete transcript fragments such as text ending in...or a trailing connector like “and”, plus complete non-actionable closings like “I’ll be right back” or “bye”. Requests that mention a closing, such as “write a goodbye email”, still reach the agent. Closing detection uses a bounded English-language heuristic; unrecognized wording continues to the agent. Logs showforced agent consult skipped reason=...when this prevents a stale queued answer.
Voice messages
Discord voice messages show a waveform preview and require OGG/Opus audio. OpenClaw generates the waveform automatically, but needsffmpeg and ffprobe on the gateway host to inspect and convert.
- Provide a local file path (URLs are rejected).
- Omit text content (Discord rejects text + voice message in the same payload).
- Any audio format is accepted; OpenClaw converts to OGG/Opus as needed.