voice
The agent reads and writes only text. Whisper turns an incoming voice note into text. The send_voice tool turns text into an ogg file, which the chat platform delivers as a voice note.
Whisper turns a voice note into text before the agent reads it
When a voice note arrives at an adapter, such as Telegram, WhatsApp or Discord, the adapter downloads the audio and gives it to routd. If VOICE_TRANSCRIPTION_ENABLED=true, routd posts the audio to the Whisper server at WHISPER_BASE_URL before the agent sees the message. The transcript comes back as plain text, and routd adds it to the attachment tag in the prompt:
<attachment path="/home/node/media/20260515/note.ogg"
mime="audio/ogg"
transcript="hey can you check the inbox">
The agent reads transcript= like any other text and never transcribes again. The raw audio stays on disk in media/<date>/ in case a skill needs the bytes.
send_voice turns text into a voice note
To reply with voice, the agent calls the send_voice MCP tool with the JID of the chat, the text and an optional voice name. routd does the rest:
routdpicks the voice. An explicit argument wins. Otherwise,routdusesvoice:from thePERSONA.mdfrontmatter, and otherwise theTTS_VOICEenvironment variable.routdhashes the text, voice and model with sha256. If<data_dir>/tts/<hash>.oggalready exists,routduses it again and synthesizes nothing.- If the file does not exist,
routdposts toTTS_BASE_URLat the OpenAI-shaped path/v1/audio/speech. The bundledttsddaemon forwards the request to a local Kokoro-FastAPI container. routdgives the ogg file to the channel adapter, which sends it with the native voice message of the platform.
The text limit is 5000 characters. The TTS endpoint is compatible with OpenAI, so to use Piper, Coqui or the OpenAI cloud, change TTS_BACKEND_URL on ttsd.
Telegram, WhatsApp and Discord deliver voice notes
| Platform | How send_voice is delivered |
|---|---|
| Telegram | sendVoice, with the push-to-talk UI |
audio with ptt:true through Baileys | |
| Discord | an audio/ogg attachment |
| Every other adapter, such as Slack and email | not supported: chanlib.ErrUnsupported, and the agent uses send instead |
The agent calls send_voice when its instructions say so
The system never turns a text reply into voice by itself. The agent chooses send_voice or send for each reply.
Two failures have two different results
With TTS_ENABLED=false on the instance, send_voice returns an “unsupported” error with a hint, and the agent sends plain text with send. If ttsd itself is down, nothing turns voice off for you. routd does not watch the /health of ttsd, so the call fails with a synthesis error, and the agent must send text instead.
Related pages cover the persona and the address
The voice: frontmatter that sets a voice per agent lives in PERSONA.md. ant shows the folder layout. jid shows the shape of the JIDs in send_voice(chatJid, …).