The two layers
Control plane
Join and leave voice channels, read the roster, react to members coming and going, set the bot’s own mute flag. Pure JavaScript. No native dependency, no extra install, works on any supported Node host including Alpine.
Audio media layer
Publish sound into the channel, and (with the right grant) decrypt what members say. Opt-in. Needs an optional native engine at an exact version, plus a patch you apply in your own app.
joinVoice() returns a credential and stops. It sends no media of any kind. If all your bot needs is presence and roster awareness, you are done at the control plane and you can skip everything native.
The security model, in two clauses
Read both. Quoting either one alone misleads whoever reads it.A bot without
voice_listen holds only its own frame key and is cryptographically unable to decrypt any member. A bot granted voice_listen is given the room key and can decrypt everyone in the channel, by design.voice_listen means “give this process the ability to decrypt us”. A server owner who installs a listening bot on the strength of “bots cannot hear you” has been misled. See Voice keys and scopes for what per-participant keys do and do not buy.
The permission ladder
Three separate grants, and they do three different things.voice_connect plus channel view gets the bot in. Hearing is a separate, third decision. A bot is deaf by default and publishing is unaffected by that: a music or text-to-speech bot works perfectly while deaf, and that is the intended configuration rather than a degraded one.
voice_listen (code 30) and event_invite (code 29) are the two newest permissions. See Permission names for the complete list.What a voice bot can do
- Join and leave a voice channel, and know when a moderator moves or removes it.
- Read the per-channel roster, including mute, deafen, and server-mute flags.
- Set its own mute and deafen flags.
- Publish microphone audio from a file, a direct media URL, a streaming-site page, or raw PCM you generate.
- With
voice_listengranted, subscribe to and decrypt every member in the channel.
What a voice bot cannot do
- Publish video or screenshare. Denied at the credential.
- Detect that nobody can decrypt its audio. The engine surfaces no per-participant encryption failure state, so silence is the only symptom. See Voice runtime.
- Hear anyone without
voice_listen, no matter what flags it sets locally. - Hear anyone while joined deafened, even holding the room key. The subscribe grant is baked into the credential when the bot joins.
What the SDK deliberately does not ship
- No recorder, no file sink, no transcription helper. Subscribing is in scope. What a bot does with decrypted member audio is your business and your users’ consent problem.
- No voice health check and no retry-on-decrypt-failure path. Both would be theatre, because the engine reports nothing to build them on. The defence is to be correct by construction, which is what
VoiceMediaSession.attach()enforces.
Next
Joining voice
The control plane: join, leave, roster, events. No native dependency.
Voice keys and scopes
Where the frame key comes from, and the one line of code that produces silence.
Publishing audio
VoiceMediaSession, the player, and the resource helpers.Voice runtime
The mandatory engine patch, host requirements, and a symptom-to-cause table.