Discord Voice Channels: Realtime Voice and Voice Messages
Covers realtime Discord voice channels, auto-join, realtime modes, and voice message attachments. Useful for operators setting up the bot to join and speak in voice channels.
Read this when
- Setting up a realtime voice conversation in a Discord voice channel
- Handling inbound Discord voice message attachments
Realtime voice conversations inside Discord voice channels, along with the separate voice message attachment format.
Voice
Discord offers two different voice surfaces: realtime voice channels (continuous conversations) and voice message attachments (the waveform preview format). Both are supported by the gateway.
Voice channels
Setup checklist:
- Turn on Message Content Intent in the Discord Developer Portal.
- Turn on Server Members Intent when role/user allowlists are in use.
- Invite the bot using the
botandapplications.commandsscopes. - In the target voice channel, grant Connect, Speak, Send Messages, and Read Message History.
- Turn on native commands (
commands.nativeorchannels.discord.commands.native). - Configure
channels.discord.voice.
Sessions are controlled with /vc join|leave|status. This command runs on the account default agent and obeys the same allowlist and group policy rules as every other Discord command. Enter these as slash commands in a Discord message box, not in a shell:
/vc join channel:<voice-channel-id>
/vc status
/vc leave
To check which permissions the bot effectively holds before it joins:
openclaw channels capabilities --channel discord --target channel:<voice-channel-id>
Auto-join example:
{
channels: {
discord: {
voice: {
enabled: true,
model: "openai/gpt-6-astra",
autoJoin: [
{
guildId: "123456789012345678",
channelId: "234567890123456789",
whenOccupied: true,
},
],
allowedChannels: [
{
guildId: "123456789012345678",
channelId: "234567890123456789",
},
],
daveEncryption: true,
decryptionFailureTolerance: 24,
connectTimeoutMs: 30000,
reconnectGraceMs: 15000,
realtime: {
provider: "openai",
model: "gpt-realtime-2.1",
speakerVoice: "cedar",
},
},
},
},
}
GPT-Live in Discord
The same GPT-Live model and voice used by Talk can also serve Discord,
alongside Google Meet and Voice Call.
Provider resolution, native delegation, and interruption policy are shared across these surfaces, though each keeps its own audio transport. Discord configurations that are not pinned retain the provider's existing default; choose GPT-Live explicitly.
For the Codex
GPT-Live route with cove, sign in using
openclaw models auth login --provider openai, then configure:
{
channels: {
discord: {
voice: {
enabled: true,
realtime: {
provider: "openai",
model: "gpt-live-1-codex",
speakerVoice: "cove",
},
},
},
},
}
That route reuses Talk's Gateway-owned WebRTC bridge, trying ChatGPT OAuth first
and falling back to a Platform API key. For the public API, pick gpt-live-1 plus a
supported public voice such as marin; Discord then uses the Gateway's direct
Platform-key WebSocket bridge. Model and voice have to come from the same
route: cove is a Codex GPT-Live voice, so pairing it with gpt-live-1
falls back to marin. See OpenAI voice and speech.
Response timing and interruption are owned by GPT-Live. Discord plays its continuous audio without waiting for a completed-response event, and adds no local speaker-start interruption. Microphone audio stays admitted during playback so that GPT-Live itself can hear and handle interruptions. Delegated tasks run on the routed OpenClaw agent with the originating speaker's Discord identity and tool permissions. Room controls clear buffered local playback before they request their spoken result; the continuous provider stream is left open so it can deliver that result. Microphone input is paced continuously by the Gateway, including silence between speaker captures. Playback keeps quiet PCM inside an active stream, including pauses delivered after earlier speech has already played. Once no unheard speech remains for the player's two-second idle grace, accepted audio drains before the room player is released. Speech that arrives during that retirement queues under a fresh resource, so it is not discarded with the old stream. Continuous playback builds a 120 ms startup buffer to absorb brief delivery gaps. A 120 ms startup deadline keeps short replies from waiting for a completed response event.
Host-enforced wake-name gating and a forced consult before every reply are not supported by GPT-Live. Those policies are off in its defaults. Setting
requireWakeName: true or consultPolicy: "always" explicitly fails at startup with a
configuration error; remove those settings, or choose gpt-realtime-2.1 when
host-controlled turns are needed in a shared meeting. Every speaker still gets a
separate voice-model connection; shared room context comes from the OpenClaw agent conversation.
Notes:
- The OpenAI
agent-proxyresponse and wake-name rules listed below need a GA realtime model, for examplegpt-realtime-2.1. GPT-Live instead uses the provider-owned response and delegation flow covered earlier. - Text-only configurations keep Discord voice off by default. To turn on
/vccommands, the voice runtime, and theGuildVoiceStatesgateway intent, setchannels.discord.voice.enabled=true, or leave an existingchannels.discord.voiceblock in place.channels.discord.intents.voiceStatescan override the intent subscription outright; when it is left unset, the effective voice enablement decides. - Which conversation path runs is governed by
voice.mode. With the default,agent-proxy, a realtime voice front end owns turn timing, interruption, and playback, hands substantive work to the routed OpenClaw agent viaopenclaw_agent_consult, and treats the outcome as if that speaker had typed a Discord prompt. The older batch STT plus TTS flow is kept bystt-tts. Withbidi, the realtime model talks directly whileopenclaw_agent_consultis exposed for the OpenClaw brain. - Because realtime voice gives every speaker a separate provider connection, delayed transcripts and tool calls still carry that speaker's Discord identity. The routed OpenClaw agent conversation and a single room playback queue are shared by everyone. History for direct
bidiconversation sits with each speaker's realtime connection; shared room history comes from the OpenClaw agent consult. More speakers means more provider connections may be consumed, and provider account limits still apply. A room holds no more than eight speaker connections, and idle ones are reclaimed after their captures, requests, and playback complete. - The OpenClaw conversation that receives voice turns is selected by
voice.agentSession. When it is unset, the voice channel's own session is used; setting{ mode: "target", target: "channel:<text-channel-id>" }instead makes the voice channel serve as the microphone and speaker extension of an existing Discord text channel session, for example#maintainers. - Suppose a realtime voice call closes while an agent reply is still in progress. That reply then completes in the voice channel's built-in text chat, and
voice.agentSession.targetdoes not alter where it lands. Cancelling a task explicitly delivers nothing. Should text-in-voice be unavailable, or the bot lack posting permission, OpenClaw writes a warning and discards the reply rather than redirecting it to another channel. - To steer a running agent from Discord voice, the backend must be able to recheck the speaker's permissions at dispatch time. Copilot does not have this guarded input capability yet; status and cancellation still work, and a new request can be started.
- For Discord voice responses and realtime consults,
voice.modeloverrides the OpenClaw agent brain. Inheriting the routed agent model happens when it is left unset. This setting is distinct fromvoice.realtime.model. - With
voice.followUsers, the bot can join, move, and leave Discord voice alongside selected users. Refer to Follow users in voice. - Speech is routed through
discord-voicebyagent-proxy, which keeps normal owner/tool authorization for the speaker and the target session while hiding the agentttstool, since Discord voice handles playback. By default,agent-proxygrants the consult full owner-equivalent tool access for owner speakers (voice.realtime.toolPolicy: "owner"). For models with host-controlled turns, consulting the OpenClaw agent before substantive answers is strongly preferred (voice.realtime.consultPolicy: "always"); GPT-Live relies on provider-owned delegation withconsultPolicy: "auto". Underalwaysmode, the realtime layer captures and transcribes speech without auto-speaking filler ahead of the consult answer, then speaks the routed OpenClaw answer. Should several forced consult answers complete while Discord is still playing the first one, the later exact-speech answers are queued until playback goes idle, so speech is never replaced mid-sentence. - When Discord playback lags temporarily, realtime voice buffers the generated audio and rides out short provider or network gaps. Buffered audio stays with each provider response, native tool continuations included. Ordinary backpressure will not cancel a response, and queued answers wait for Discord to finish the previous answer even if that answer's provider response or audio encoder has already completed.
- For GA OpenAI Realtime and xAI, an interruption truncates every retained native audio item at the amount Discord consumed. Items in the queue are dropped at zero, while replies that already finished playing stay untouched. Playback progress carries across temporary gaps within the same response; the echo guard in GA OpenAI Realtime relies on the combined consumed duration of the retained items. GPT-Live interrupts natively and does not truncate items.
- A failed realtime connection for one speaker leaves the other speakers connected. Look at the
realtime speaker failedlog, then speak again to establish a new connection. If the initial provider connection fails during/vc join, the join fails, though a recorder that is already connected keeps recording; consult therealtime session failed terminallylog and retry/vc join. The Discord voice session is not ended by temporary provider reconnects. - Under
stt-ttsmode, transcription is handled bytools.media.audio;voice.modelhas no effect on it. stt-ttsreplies stay active until Discord has played them out, so lengthy responses are not truncated by a fixed one-minute playback limit.- For realtime modes, the realtime audio session is configured by
voice.realtime.provider,voice.realtime.model, andvoice.realtime.speakerVoice. When using OpenAI Realtime 2.1 with the Codex brain, setvoice.realtime.model: "gpt-realtime-2.1"andvoice.model: "openai/gpt-6-astra". - Where the host offers a shared agent-context resolver, realtime voice modes use it. It informs the model that it speaks for an OpenClaw agent with multiple sessions and, by default, includes the small profile files
IDENTITY.md,USER.md, andSOUL.md, so quick direct turns preserve the same identity, user grounding, and persona as the routed agent. Customize the profile files by settingvoice.realtime.bootstrapContextFilesto a subset, or omit them with[]while retaining the agent-context paragraph. On OpenClaw 2026.9.6 the plugin keeps that host's profile-only context behavior;[]omits the context, and the newer multi-session paragraph is not available. Both paths rely on the host's bootstrap hooks and workspace access. This setting supports only those profile files;AGENTS.mdremains in the normal agent context. For workspace work, current facts, memory lookup, or tool-backed actions, the injected profile context is no substitute foropenclaw_agent_consult. - Wake-name gating in OpenAI
agent-proxyrealtime mode adapts to the room by default: a single human may talk without a wake name, but two or more humans must begin or end a turn with one. Other bots are not counted as people. Choosevoice.realtime.requireWakeName: trueto always require a wake name, orfalseto never require one. A configured wake name must be one or two words. Whenvoice.realtime.wakeNamesis unset, OpenClaw uses the routed agentnameplusOpenClaw, falling back to the agent id plusOpenClaw. While a wake-name gate is active, realtime provider auto-response is disabled, accepted turns go through the OpenClaw agent consult path, and a short spoken acknowledgement is given 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 never triggers an acknowledgement. Live joins and leaves are followed without reconnecting voice. - Current Realtime 2 event names and legacy Codex-compatible aliases for output audio and transcript events are both accepted by the OpenAI realtime provider, which lets compatible provider snapshots drift without losing assistant audio.
- With response-based models,
voice.realtime.bargeIndecides whether audible microphone input interrupts active realtime playback. Silent packets and speaker-start notifications by themselves do not interrupt it. When unset, the realtime provider's input-audio interruption setting applies. GPT-Live ignores this setting, as it owns interruption. voice.realtime.minBargeInAudioEndMssets the shortest assistant playback time before a barge-in from GA OpenAI Realtime cuts the audio off. Default:250. Use0to allow instant interruption in rooms with little echo, or increase it where speakers produce heavy echo. GPT-Live is unaffected by this setting.voice.ttstakes precedence overtts, but only forstt-ttsvoice playback; realtime modes rely onvoice.realtime.speakerVoiceinstead. To get an OpenAI voice during Discord playback, configurevoice.tts.provider: "openai"and pick a Text-to-speech voice invoice.tts.providers.openai.speakerVoice. On the current OpenAI TTS model,cedarworks well when you want a masculine-sounding voice.- Discord
systemPromptoverrides set per channel apply to the voice transcript turns of that channel. - On joining a voice channel, OpenClaw delivers a silent system event to the routed agent session containing the current participant roster. Participants who join or leave afterward update that session without causing an unprompted spoken reply; Discord display names count as untrusted labels. Each authorized voice turn also gets a fresh roster snapshot.
- Owner status for voice transcript turns and
/vccommands comes from Discord entries incommands.ownerAllowFrom. If no Discord command owner exists, the selected Discord account'sallowFrom(or the legacydm.allowFrom) can still grant voice access, though it does not confer owner status. Which tools the agent can see follows the tool policy configured for the routed session. - Without any command-owner configuration, authorized speakers may ask the agent to list or change the voice of the current realtime call using
talk_voice. The switch affects the shared room and leaves saved voice defaults as they are. Voice control stays tied to the admitted turn and ends when the call finishes, the turn completes or is canceled, or its access policy changes; other owner-only tools keep their normal permissions. - Voice turns that were canceled or revoked are never retried as new agent requests. Once a user leaves, pending batch turns cannot begin agent work or speech synthesis. Work already in progress may complete; transcript recording is handled separately.
- Where
voice.autoJoinlists several entries for one guild, OpenClaw joins the last channel configured for that guild. - Voice-state changes from other members never overwrite the bot's channel, mute, or deaf settings. Such updates do still feed participant rosters, occupancy-based auto-join, and follow-user behavior.
voice.autoJoin[].whenOccupieddefaults tofalse. For an auto-managed room that should hold the bot only while at least one human is present, set it totrue. OpenClaw joins when the first human arrives and leaves once the last human departs; the OpenClaw bot and other bots are not counted. Startup, fresh gateway sessions, and resumed gateway sessions all reconcile against Discord's voice-state roster.- Occupancy management only owns the sessions it joined itself. A manual
/vc join, a standalone transcript-only session, a follow-user session, an active session in a different channel, or any other ad-hoc join is neither moved nor disconnected when the configured room empties. If transcript capture is attached to an occupancy-managed session, that ownership is preserved. voice.allowedChannelsis an optional allowlist of rooms. Leaving it unset lets/vc joinjoin any authorized Discord voice channel. Once set,/vc join, startup auto-join, and bot voice-state moves are limited to the listed{ guildId, channelId }entries. An empty array denies every Discord voice join. Should Discord move the bot outside the allowlist, OpenClaw exits that channel and, when an auto-join target is available, rejoins it.voice.daveEncryptionandvoice.decryptionFailureToleranceare forwarded to@discordjs/voicejoin options; their upstream defaults aredaveEncryption=trueanddecryptionFailureTolerance=24.- For Discord voice receive and realtime raw PCM playback, OpenClaw relies on the bundled
libopus-wasmcodec. A pinned libopus WebAssembly build is included, so no native opus addons are needed. Voice sockets, codecs, playback conversion, and packet pacing for Discord all execute in a worker thread. GPT-Live continuous output goes straight from its media worker to the Discord playback worker instead of passing every audio chunk through the Gateway event loop. Speaker admission, agent work, and transcripts stay on the Gateway, so a busy Gateway can still delay those control operations. - DAVE's native encryption pool spawns one Rayon thread per CPU on the first membership rekey and sits outside JavaScript Worker counts. To bound it, operators can set
RAYON_NUM_THREADSin the Gateway service environment; OpenClaw does not define this variable. Child processes that inherit that environment are subject to the same limit. voice.connectTimeoutMssets how long the initial@discordjs/voiceReady wait lasts for/vc joinand auto-join attempts. Default:30000.voice.reconnectGraceMssets the wait OpenClaw allows for a disconnected voice session to start reconnecting before it destroys that session. Default:15000.- Under
stt-ttsmode, another user starting to speak does not by itself halt voice playback. To prevent feedback loops, OpenClaw admits no new conversational turns while TTS is playing; a capture that was started explicitly will still record that speech. Wait for playback to end before taking the next conversational turn. When interruption is enabled, response-based realtime models treat audible authorized microphone input as barge-in signals; GPT-Live processes incoming audio on its own. - With GA OpenAI Realtime, speaker echo picked up by an open mic can register as barge-in and cut playback. In echo-heavy Discord rooms, set
voice.realtime.providers.openai.interruptResponseOnInputAudio: falseso the provider does not auto-interrupt on input audio. Addvoice.realtime.bargeIn: trueif audible Discord microphone input should still interrupt active playback. Playback truncations shorter thanvoice.realtime.minBargeInAudioEndMsare treated by the GA OpenAI realtime bridge as probable echo or noise: it logs them as skipped rather than clearing Discord playback. None of these controls override GPT-Live's native interruption. voice.captureSilenceGraceMssets the delay after Discord reports a speaker stopped before OpenClaw finalizes that audio segment for STT. Default:2000; increase it when Discord breaks ordinary pauses into choppy partial transcripts.- When ElevenLabs is the chosen TTS provider, Discord voice playback streams TTS and begins from the provider response stream. Providers lacking streaming support use the synthesized temp-file path instead.
- OpenClaw monitors receive decrypt failures and recovers automatically: after repeated failures within a short window, it leaves and rejoins the voice channel.
- Should receive logs keep showing
DecryptionFailed(UnencryptedWhenPassthroughDisabled)after an update, gather a dependency report and logs. The bundled@discordjs/voiceline carries the upstream padding fix from discord.js PR #11449, which resolved discord.js issue #11419. The operation was abortedreceive events are normal when OpenClaw finalizes a captured speaker segment; treat them as verbose diagnostics rather than warnings.- Verbose Discord voice logs carry a bounded one-line STT transcript preview for every accepted speaker segment, so debugging reveals both the user side and the agent reply side without dumping unbounded transcript text.
- Under
agent-proxymode, forced consult fallback discards likely incomplete transcript fragments, such as text ending in...or a trailing connector like "and", along with complete non-actionable closings like "I'll be right back" or "bye". A request that mentions a closing, such as "write a goodbye email", still reaches the agent. Closing detection relies on a bounded English-language heuristic; wording it does not recognize continues to the agent. When this blocks a stale queued answer, logs showforced agent consult skipped reason=....
Voice messages
Discord voice messages require OGG/Opus audio and display a waveform preview. OpenClaw builds the waveform on its own, but ffmpeg and ffprobe must be present on the gateway host to inspect and convert.
- Supply a local file path (URLs are refused).
- Leave out text content (Discord refuses text plus a voice message in one payload).
- Any audio format is fine; OpenClaw converts it to OGG/Opus when needed.
The agent sends a voice message through the message tool, not from a shell:
message(action="send", channel="discord", target="channel:123", path="/path/to/audio.mp3", asVoice=true)