stt-tts mode (talk.speak calls this
same synthesis path). Provider-native realtime Talk sessions synthesize
speech inside the realtime provider instead; transcription sessions never
synthesize an assistant voice reply.
Quick start
1
Pick a provider
OpenAI and ElevenLabs are the most reliable hosted options. Microsoft and
Local CLI work without an API key. See the provider matrix
for the full list.
2
Set the API key
Export the env var for your provider (for example
OPENAI_API_KEY,
ELEVENLABS_API_KEY). Microsoft and Local CLI need no key.3
Enable in config
Set
tts.auto: "always" and tts.provider:4
Try it in chat
/tts status shows the current state. /tts audio Hello from OpenClaw
sends a one-off audio reply.Auto-TTS is off by default. When
tts.provider is unset,
OpenClaw picks the first configured provider in registry auto-select order.
The built-in tts agent tool is explicit-intent only: ordinary chat stays
text unless the user asks for audio, uses /tts, or enables Auto-TTS/directive
speech.Supported providers
If multiple providers are configured, the selected one is used first and the
others are fallback options. Auto-summary uses
summaryModel (or
agents.defaults.model.primary), so that provider must also be authenticated
if you keep summaries enabled.
Configuration
TTS config lives undertts in ~/.openclaw/openclaw.json. Pick a
preset and adapt the provider block. The speakerVoice/speakerVoiceId
fields shown below are canonical; each provider’s own voice/voiceId/
voiceName field names still work as legacy aliases.
- Azure Speech
- ElevenLabs
- Fish Audio
- Google Gemini
- Gradium
- Inworld
- Local CLI
- Microsoft (no key)
- MiniMax
- OpenAI + ElevenLabs
- OpenRouter
- Volcengine
- xAI
- Xiaomi MiMo
mimo-v2.5-tts-voicedesign, omit speakerVoice and set style to
the voice-design prompt. OpenClaw sends that prompt as the TTS user message
and does not send audio.voice for the voicedesign model.
Local Speech Swift and speech-core
Speech Swift and speech-core provide local speech inference across macOS, Linux, and Windows. Use the OpenAI-compatible HTTP provider when Speech Swift and OpenClaw run on the same Mac. Use Local CLI for direct executable integration on any supported host. Installffmpeg when a channel needs OpenClaw to convert WAV output to Opus or
raw PCM.
- macOS HTTP
- macOS CLI
- Linux CLI
- Windows CLI
Start Speech Swift’s local server:Point the OpenAI speech provider at its loopback endpoint.
responseFormat
must be wav because the local endpoint does not emit compressed audio:tts-1 selects Kokoro. Speech Swift registry aliases such as qwen3-tts,
cosyvoice, and voxcpm2 select other local engines. The placeholder API key
is required by OpenClaw’s provider configuration but is not validated by the
loopback server.Per-agent voice overrides
Useagents.entries.*.tts when one agent should speak with a different provider,
voice, model, persona, or auto-TTS mode. The agent block deep-merges over
tts, so provider credentials can stay in the global provider config:
agents.entries.*.tts.persona alongside provider
config — it overrides the global tts.persona for that agent only.
Precedence order for automatic replies, /tts audio, /tts status, and the
tts agent tool:
tts- active
agents.entries.*.tts - channel override, when the channel supports
channels.<channel>.tts - account override, when the channel passes
channels.<channel>.accounts.<id>.tts - local
/ttspreferences for this host - inline
[[tts:...]]directives when model overrides are enabled
tts and
deep-merge over the earlier layers, so shared provider credentials can stay in
tts while a channel or bot account changes only speaker voice, model, persona,
or auto mode:
Personas
A persona is a stable spoken identity that can be applied deterministically across providers. It can prefer one provider, define provider-neutral prompt intent, and carry provider-specific bindings for voices, models, prompt templates, seeds, and voice settings.Minimal persona
Full persona (provider-specific shaping)
Persona resolution
The active persona is selected deterministically:/tts persona <id>local preference, if set.tts.persona, if set.- No persona.
- Direct overrides (CLI, gateway, Talk, allowed TTS directives).
/tts provider <id>local preference.- Active persona’s
provider. tts.provider.- Registry auto-select.
tts.providers.<id>tts.personas.<persona>.providers.<id>- Trusted request overrides
- Allowed model-emitted TTS directive overrides
Custom persona shaping
Provider-neutralpersonas.<id>.prompt.* config is retired. Doctor removes
those fields and points to the speech-provider seam. Put built-in provider
settings under personas.<id>.providers.<provider> (for example Google
personaPrompt or OpenAI instructions). For custom shaping, implement a
speech provider plugin with prepareSynthesis(ctx) and return adjusted text,
provider config, or overrides before synthesize() runs. This keeps expressive
prompt construction in provider code where request semantics are known.
Fallback policy
fallbackPolicy controls behavior when a persona has no binding for the
attempted provider:
The whole TTS request only fails when every attempted provider is skipped
or fails.
Talk session provider selection is session-scoped. A Talk client should choose
provider ids, model ids, voice ids, and locales from
talk.catalog and pass
them through the Talk session or handoff request. Opening a voice session should
not mutate tts or global Talk provider defaults.
Model-driven directives
By default, the assistant can emit[[tts:...]] directives to override
voice, model, or speed for a single reply, plus an optional
[[tts:text]]...[[/tts:text]] block for expressive cues that should appear in
audio only:
tts.auto is "tagged", directives are required to trigger
audio. Streaming block delivery strips directives from visible text before the
channel sees them, even when split across adjacent blocks.
provider=... is ignored unless modelOverrides.allowProvider: true. When a
reply declares provider=..., the other keys in that directive are parsed
only by that provider; unsupported keys are stripped and reported as TTS
directive warnings.
Available directive keys:
provider(registered provider id; requiresallowProvider: true)speakerVoice/speakerVoiceId(legacy aliases:voice,voiceName,voice_name,google_voice,voiceId)model/google_modelstability,similarityBoost,style,speed,useSpeakerBoostvol/volume(MiniMax volume,(0, 10])pitch(MiniMax integer pitch, −12 to 12; fractional values are truncated)emotion(Volcengine emotion tag)applyTextNormalization(auto|on|off)languageCode(ISO 639-1)seed
Slash commands
Single command/tts. On Discord, OpenClaw also registers /voice because
/tts is a built-in Discord command — text /tts ... still works.
Commands require an authorized sender (allowlist/owner rules apply) and either
commands.text or native command registration must be enabled./tts onwrites the local TTS preference toalways;/tts offwrites it tooff./tts chat on|off|defaultwrites a session-scoped auto-TTS override for the current chat./tts persona <id>writes the local persona preference;/tts persona offclears it./tts latestreads the latest assistant reply from the current session transcript and sends it as audio once. It stores only a hash of that reply on the session entry to suppress duplicate voice sends./tts audiogenerates a one-off audio reply (does not toggle TTS on)./tts limit <chars>accepts 100–4096 (4096 is the Telegram caption/message max); values outside that range are rejected.limitandsummaryare stored in local prefs, not the main config./tts statusincludes fallback diagnostics for the latest attempt —Fallback: <primary> -> <used>,Attempts: ..., and per-attempt detail (provider:outcome(reasonCode) latency)./statusshows the active TTS mode plus configured provider, model, voice, and sanitized custom endpoint metadata when TTS is enabled.
Per-user preferences
Slash commands write local overrides to the TTS preferences path. The default is~/.openclaw/settings/tts.json; override it with OPENCLAW_TTS_PREFS. Doctor
moves the retired global tts.prefsPath value into shared machine state.
Advanced multi-agent setups may still set agents.entries.<id>.tts.prefsPath
when agents intentionally use separate preference stores.
These override the effective config from
tts plus the active
agents.entries.*.tts block for that host.
Output formats
TTS voice delivery is channel-capability driven. Channel plugins advertise whether voice-style TTS should ask providers for a nativevoice-note target or
keep normal audio-file synthesis, and whether the channel transcodes
non-native output before sending.
After synthesis, OpenClaw persists batch TTS output in the media store under
tool-speech-synthesis. The reply uses that stable media path instead of a
provider temporary file, and normal media maintenance prunes expired output.
Local CLI providers may still use {{OutputPath}} as scratch space before
OpenClaw imports the completed bytes. See Media playback
for inline-player formats and limits.
Per-provider notes:
- Feishu / WhatsApp transcoding: when a voice-note reply lands as MP3/WebM/WAV/M4A or another likely audio file, the channel plugin transcodes it to 48 kHz Ogg/Opus with
ffmpeg(libopus, 64 kbps) before sending the native voice message. WhatsApp sends the result through the Baileysaudiopayload withptt: trueandaudio/ogg; codecs=opus. On transcode failure: Feishu catches the error and falls back to sending the original file as a plain attachment; WhatsApp has no fallback, so the send itself fails rather than posting an incompatible PTT payload. - MiniMax: MP3 (
speech-2.8-hdmodel, 32 kHz sample rate) for normal audio attachments; transcoded to 48 kHz Opus withffmpegfor channel-advertised voice-note targets. - Xiaomi MiMo: MP3 by default, or WAV when configured; transcoded to 48 kHz Opus with
ffmpegfor channel-advertised voice-note targets. - Local CLI: uses the configured
outputFormat. Voice-note targets are converted to Ogg/Opus and telephony output is converted to raw 16 kHz mono PCM withffmpeg. - Google Gemini: returns raw 24 kHz PCM. OpenClaw wraps it as WAV for audio attachments, transcodes it to 48 kHz Opus for voice-note targets, and returns PCM directly for Talk/telephony.
- Gradium: WAV for audio attachments, Opus for voice-note targets, and
ulaw_8000at 8 kHz for telephony. - Inworld: MP3 for normal audio attachments, native
OGG_OPUSfor voice-note targets, and rawPCMat 22050 Hz for Talk/telephony. - xAI: MP3 by default; audio-file synthesis may use
mp3,wav,pcm,mulaw, oralawfor both buffered and streaming output. Voice-note targets use MP3 for streaming and buffered fallback because xAI’spcm,mulaw, andalawoutputs are headerless raw audio. Buffered synthesis uses xAI’s batch REST/v1/ttsendpoint;textToSpeechStreamuses nativewss://api.x.ai/v1/tts. This is not the realtime voice contract. Native Opus voice-note format is not supported. - Microsoft: uses
microsoft.outputFormat(defaultaudio-24khz-48kbitrate-mono-mp3).- The bundled transport accepts an
outputFormat, but not all formats are available from the service. - Output format values follow Microsoft Speech output formats (including Ogg/WebM Opus).
- Telegram
sendVoiceaccepts OGG/MP3/M4A; use OpenAI/ElevenLabs if you need guaranteed Opus voice messages. - If the configured Microsoft output format fails, OpenClaw retries with MP3.
- When no explicit voice override is set and the default English voice is used, OpenClaw auto-switches to a Chinese neural voice (
zh-CN-XiaoxiaoNeural,zh-CNlocale) if the reply text is CJK-dominant.
- The bundled transport accepts an
responseFormat overrides that selection; a format that is not
voice-note compatible may be delivered as an audio file or transcoded by a
channel that supports conversion.
Auto-TTS behavior
Whentts.auto is enabled, OpenClaw:
- Skips TTS if the reply already contains structured media.
- Skips very short replies (under 10 chars).
- Summarizes long replies when summaries are enabled, using
summaryModel(oragents.defaults.model.primary). - Attaches the generated audio to the reply.
- In
mode: "final", still sends audio-only TTS for streamed final replies after the text stream completes; the generated media goes through the same channel media normalization as normal reply attachments.
maxLength, OpenClaw never skips audio outright:
- Summary on (default) and a summary model is available: summarizes the
text to roughly
maxLengthchars, then synthesizes the summary. - Summary off, summarization fails, or no API key is available for the
summary model: truncates the text to
maxLengthchars and synthesizes the truncated text.
Field reference
Top-level tts.*
Top-level tts.*
"off" | "always" | "inbound" | "tagged"
Auto-TTS mode.
inbound only sends audio after an inbound voice message; tagged only sends audio when the reply includes [[tts:...]] directives or a [[tts:text]] block.boolean
deprecated
Legacy toggle.
openclaw doctor --fix migrates this to auto."final" | "all"
default:"final"
"all" includes tool/block replies in addition to final replies.string
Speech provider id. When unset, OpenClaw uses the first configured provider in registry auto-select order. Legacy
provider: "edge" is rewritten to "microsoft" by openclaw doctor --fix.string
Active persona id from
personas. Normalized to lowercase.object
Stable spoken identity. Fields:
label, description, provider, fallbackPolicy, prompt, providers.<provider>. See Personas.string
Cheap model for auto-summary; defaults to
agents.defaults.model.primary. Accepts provider/model or a configured model alias.object
Allow the model to emit TTS directives.
enabled defaults to true; allowProvider defaults to false.object
Provider-owned settings keyed by speech provider id. Legacy direct blocks (
tts.openai, .elevenlabs, .microsoft, .edge) are rewritten by openclaw doctor --fix; commit only tts.providers.<id>.number
default:"4096"
Hard cap for TTS input characters.
/tts audio, tts.convert, and tts.speak fail if exceeded.number
default:"30000"
Request timeout in milliseconds. A per-call
timeoutMs (agent tool, gateway) wins when set; otherwise an explicitly configured tts.timeoutMs wins over any plugin-authored provider default.apiKey fields can be raw strings or SecretRefs. During cold Gateway
startup, an unavailable TTS SecretRef marks the built-in TTS capability
configured-unavailable instead of stopping the Gateway. tts.speak then returns
UNAVAILABLE with reason SECRET_SURFACE_UNAVAILABLE, and no provider request is
sent. Status and doctor list the degraded TTS owner and its config paths. The
explicit refs remain in the runtime snapshot, so environment or profile
credentials cannot silently select a different account. Reloads and config-write
preflight apply the owner-aware degradation policy: an unchanged eligible TTS
owner may keep its last-known-good credentials as stale, while a new or changed
failure becomes cold without blocking healthy owners. Structurally invalid refs
and resolved values still fail startup or reject the update.Azure Speech
Azure Speech
string
Env:
AZURE_SPEECH_KEY, AZURE_SPEECH_API_KEY, or SPEECH_KEY.string
Azure Speech region (e.g.
eastus). Env: AZURE_SPEECH_REGION or SPEECH_REGION.string
Optional Azure Speech endpoint override (alias
baseUrl).string
Azure voice ShortName. Default
en-US-JennyNeural. Legacy alias: voice.string
SSML language code. Default
en-US.string
Azure
X-Microsoft-OutputFormat for standard audio. Default audio-24khz-48kbitrate-mono-mp3.string
Azure
X-Microsoft-OutputFormat for voice-note output. Default ogg-24khz-16bit-mono-opus.ElevenLabs
ElevenLabs
string
Falls back to
ELEVENLABS_API_KEY or XI_API_KEY.string
Model id. Default
eleven_multilingual_v2. Legacy ids eleven_turbo_v2_5/eleven_turbo_v2 are normalized to the matching flash model.string
ElevenLabs voice id. Default
pMsXgVXv3BLzUgSXRplE. Legacy alias: voiceId.object
stability, similarityBoost, style (each 0..1, defaults 0.5/0.75/0), useSpeakerBoost (true|false, default true), speed (0.5..2.0, default 1.0)."auto" | "on" | "off"
Text normalization mode.
string
2-letter ISO 639-1 (e.g.
en, de).number
Integer
0..4294967295 for best-effort determinism.string
Override ElevenLabs API base URL.
Google Gemini
Google Gemini
string
Falls back to
GEMINI_API_KEY / GOOGLE_API_KEY. If omitted, TTS can reuse models.providers.google.apiKey before env fallback.string
Gemini TTS model. Default
gemini-3.1-flash-tts-preview.string
Gemini prebuilt voice name. Default
Kore. Legacy aliases: voiceName, voice.string
Natural-language style prompt prepended before spoken text.
string
Optional speaker label prepended before spoken text when your prompt uses a named speaker.
"audio-profile-v1"
Set to
audio-profile-v1 to wrap active persona prompt fields in a deterministic Gemini TTS prompt structure.string
Google-specific extra persona prompt text appended to the template’s Director’s Notes.
string
Only
https://generativelanguage.googleapis.com is accepted.Gradium
Gradium
Inworld
Inworld
Local CLI (tts-local-cli)
Local CLI (tts-local-cli)
string
Local executable or command string for CLI TTS.
string[]
Command arguments. Supports
{{Text}}, {{OutputPath}}, {{OutputDir}}, {{OutputBase}} placeholders."mp3" | "opus" | "wav"
Expected CLI output format. Default
mp3 for audio attachments.number
Command timeout in milliseconds. Default
120000.string
Optional command working directory.
Record<string, string>
Optional environment overrides for the command.
Microsoft (no API key)
Microsoft (no API key)
boolean
default:"true"
Allow Microsoft speech usage.
string
Microsoft neural voice name (e.g.
en-US-MichelleNeural). Legacy alias: voice. If the default English voice is in effect and reply text is CJK-dominant, OpenClaw auto-switches to zh-CN-XiaoxiaoNeural.string
Language code (e.g.
en-US).string
Microsoft output format. Default
audio-24khz-48kbitrate-mono-mp3. Not all formats are supported by the bundled Edge-backed transport.string
Percent strings (e.g.
+10%, -5%).boolean
Write JSON subtitles alongside the audio file.
string
Proxy URL for Microsoft speech requests.
number
Request timeout override (ms).
object
deprecated
Legacy alias. Run
openclaw doctor --fix to rewrite persisted config to providers.microsoft.MiniMax
MiniMax
string
Falls back to
MINIMAX_API_KEY. Token Plan auth via MINIMAX_OAUTH_TOKEN, MINIMAX_CODE_PLAN_KEY, or MINIMAX_CODING_API_KEY.string
Default
https://api.minimax.io. Env: MINIMAX_API_HOST.string
Default
speech-2.8-hd. Env: MINIMAX_TTS_MODEL.string
Default
English_expressive_narrator. Env: MINIMAX_TTS_VOICE_ID. Legacy alias: voiceId.number
0.5..2.0. Default 1.0.number
(0, 10]. Default 1.0.number
Integer
-12..12. Default 0. Fractional values are truncated before the request.OpenAI
OpenAI
string
Falls back to
OPENAI_API_KEY.string
OpenAI TTS model id. Default
gpt-4o-mini-tts.string
Voice name (e.g.
alloy, cedar). Default coral. Legacy alias: voice.string
Explicit OpenAI
instructions field. When set, persona prompt fields are not auto-mapped."mp3" | "opus" | "wav"
Explicit response format. When omitted, OpenClaw selects Opus for voice-note targets and MP3 otherwise. Use
wav for compatible local endpoints that do not encode compressed audio.Record<string, unknown>
Extra JSON fields merged into
/audio/speech request bodies after generated OpenAI TTS fields. Use this for OpenAI-compatible endpoints such as Kokoro that require provider-specific keys like lang; unsafe prototype keys are ignored.string
Override the OpenAI TTS endpoint. Resolution order: config →
OPENAI_TTS_BASE_URL → https://api.openai.com/v1. Non-default values are treated as OpenAI-compatible TTS endpoints, so custom model and voice names are accepted, and speed loses its 0.25..4.0 range check.OpenRouter
OpenRouter
string
Env:
OPENROUTER_API_KEY. Can reuse models.providers.openrouter.apiKey.string
Default
https://openrouter.ai/api/v1. Legacy https://openrouter.ai/v1 is normalized.string
Default
hexgrad/kokoro-82m. Alias: modelId.string
Default
af_alloy. Legacy aliases: voice, voiceId."mp3" | "pcm"
Default
mp3.number
Provider-native speed override.
Volcengine (BytePlus Seed Speech)
Volcengine (BytePlus Seed Speech)
string
Env:
VOLCENGINE_TTS_API_KEY or BYTEPLUS_SEED_SPEECH_API_KEY.string
Default
seed-tts-1.0. Env: VOLCENGINE_TTS_RESOURCE_ID. Use seed-tts-2.0 when your project has TTS 2.0 entitlement.string
App key header. Default
aGjiRDfUWi. Env: VOLCENGINE_TTS_APP_KEY.string
Override the Seed Speech TTS HTTP endpoint. Env:
VOLCENGINE_TTS_BASE_URL.string
Voice type. Default
en_female_anna_mars_bigtts. Env: VOLCENGINE_TTS_VOICE. Legacy alias: voice.number
Provider-native speed ratio,
0.2..3.string
Provider-native emotion tag.
string
deprecated
Legacy Volcengine Speech Console fields. Env:
VOLCENGINE_TTS_APPID, VOLCENGINE_TTS_TOKEN, VOLCENGINE_TTS_CLUSTER (default volcano_tts).xAI
xAI
string
Env:
XAI_API_KEY.string
Default
https://api.x.ai/v1. Env: XAI_BASE_URL.string
Default
eve. With auth, openclaw infer tts voices --provider xai fetches the current built-in catalog; without auth it lists offline fallbacks ara, eve, leo, rex, and sal. Account custom voice IDs are forwarded even when absent from the built-in list. Legacy alias: voiceId.string
BCP-47 language code or
auto. Default en."mp3" | "wav" | "pcm" | "mulaw" | "alaw"
Default
mp3.number
Provider-native speed override,
0.7..1.5.Xiaomi MiMo
Xiaomi MiMo
string
Env:
XIAOMI_API_KEY.string
Default
https://api.xiaomimimo.com/v1. Env: XIAOMI_BASE_URL.string
Default
mimo-v2.5-tts. Env: XIAOMI_TTS_MODEL. Also supports mimo-v2.5-tts-voicedesign.string
Default
mimo_default for preset-voice models. Env: XIAOMI_TTS_VOICE. Legacy alias: voice. Not sent for mimo-v2.5-tts-voicedesign."mp3" | "wav"
Default
mp3. Env: XIAOMI_TTS_FORMAT.string
Optional natural-language style instruction sent as the user message; not spoken. For
mimo-v2.5-tts-voicedesign, this is the voice-design prompt; OpenClaw supplies a default when omitted.Agent tool
Thetts tool converts text to speech and returns an audio attachment for
reply delivery. On Feishu, Matrix, Telegram, and WhatsApp, the audio is
delivered as a voice message rather than a file attachment. Feishu and
WhatsApp can transcode non-Opus TTS output on this path when ffmpeg is
available.
WhatsApp sends audio through Baileys as a PTT voice note (audio with
ptt: true) and sends visible text separately from PTT audio because
clients do not consistently render captions on voice notes.
The tool accepts optional channel and timeoutMs fields; timeoutMs is a
per-call provider request timeout in milliseconds. Per-call values override
tts.timeoutMs; configured TTS timeouts override any plugin-authored
provider default.
Gateway RPC
Service links
- Azure Speech provider
- Azure Speech REST text-to-speech
- ElevenLabs Authentication
- ElevenLabs Text to Speech
- Gradium
- Inworld TTS API
- Microsoft Speech output formats
- MiniMax T2A v2 API
- node-edge-tts
- OpenAI Audio API reference
- OpenAI text-to-speech guide
- speech-core
- Speech Swift
- Volcengine TTS HTTP API
- xAI text to speech
- Xiaomi MiMo speech synthesis