2026-03-19 10:42:46 -07:00
---
summary: "Text-to-speech (TTS) for outbound replies"
read_when:
- Enabling text-to-speech for replies
- Configuring TTS providers or limits
- Using /tts commands
title: "Text-to-Speech"
---
# Text-to-speech (TTS)
2026-03-27 16:24:56 +01:00
OpenClaw can convert outbound replies into audio using ElevenLabs, Microsoft, MiniMax, or OpenAI.
2026-03-24 15:53:01 +08:00
It works anywhere OpenClaw can send audio.
2026-03-19 10:42:46 -07:00
## Supported services
- **ElevenLabs** (primary or fallback provider)
2026-03-26 23:22:35 +00:00
- **Microsoft** (primary or fallback provider; current bundled implementation uses `node-edge-tts` )
2026-03-27 16:24:56 +01:00
- **MiniMax** (primary or fallback provider; uses the T2A v2 API)
2026-03-19 10:42:46 -07:00
- **OpenAI** (primary or fallback provider; also used for summaries)
### Microsoft speech notes
The bundled Microsoft speech provider currently uses Microsoft Edge's online
neural TTS service via the `node-edge-tts` library. It's a hosted service (not
local), uses Microsoft endpoints, and does not require an API key.
`node-edge-tts` exposes speech configuration options and output formats, but
not all options are supported by the service. Legacy config and directive input
using `edge` still works and is normalized to `microsoft` .
Because this path is a public web service without a published SLA or quota,
treat it as best-effort. If you need guaranteed limits and support, use OpenAI
or ElevenLabs.
## Optional keys
2026-03-27 16:24:56 +01:00
If you want OpenAI, ElevenLabs, or MiniMax:
2026-03-19 10:42:46 -07:00
- `ELEVENLABS_API_KEY` (or `XI_API_KEY` )
2026-03-27 16:24:56 +01:00
- `MINIMAX_API_KEY`
2026-03-19 10:42:46 -07:00
- `OPENAI_API_KEY`
2026-03-26 23:22:35 +00:00
Microsoft speech does **not ** require an API key.
2026-03-19 10:42:46 -07:00
If multiple providers are configured, the selected provider is used first and the others are fallback options.
Auto-summary uses the configured `summaryModel` (or `agents.defaults.model.primary` ),
so that provider must also be authenticated if you enable summaries.
## Service links
- [OpenAI Text-to-Speech guide ](https://platform.openai.com/docs/guides/text-to-speech )
- [OpenAI Audio API reference ](https://platform.openai.com/docs/api-reference/audio )
- [ElevenLabs Text to Speech ](https://elevenlabs.io/docs/api-reference/text-to-speech )
- [ElevenLabs Authentication ](https://elevenlabs.io/docs/api-reference/authentication )
2026-03-27 16:24:56 +01:00
- [MiniMax T2A v2 API ](https://platform.minimaxi.com/document/T2A%20V2 )
2026-03-19 10:42:46 -07:00
- [node-edge-tts ](https://github.com/SchneeHertz/node-edge-tts )
- [Microsoft Speech output formats ](https://learn.microsoft.com/azure/ai-services/speech-service/rest-text-to-speech#audio-outputs )
## Is it enabled by default?
No. Auto‑ TTS is **off ** by default. Enable it in config with
`messages.tts.auto` or per session with `/tts always` (alias: `/tts on` ).
2026-03-26 23:22:35 +00:00
When `messages.tts.provider` is unset, OpenClaw picks the first configured
speech provider in registry auto-select order.
2026-03-19 10:42:46 -07:00
## Config
TTS config lives under `messages.tts` in `openclaw.json` .
Full schema is in [Gateway configuration ](/gateway/configuration ).
### Minimal config (enable + provider)
``` json5
{
messages: {
tts: {
auto: "always",
provider: "elevenlabs",
},
},
}
```
### OpenAI primary with ElevenLabs fallback
``` json5
{
messages: {
tts: {
auto: "always",
provider: "openai",
summaryModel: "openai/gpt-4.1-mini",
modelOverrides: {
enabled: true,
},
2026-03-26 23:22:35 +00:00
providers: {
openai: {
apiKey: "openai_api_key",
baseUrl: "https://api.openai.com/v1",
model: "gpt-4o-mini-tts",
voice: "alloy",
},
elevenlabs: {
apiKey: "elevenlabs_api_key",
baseUrl: "https://api.elevenlabs.io",
voiceId: "voice_id",
modelId: "eleven_multilingual_v2",
seed: 42,
applyTextNormalization: "auto",
languageCode: "en",
voiceSettings: {
stability: 0.5,
similarityBoost: 0.75,
style: 0.0,
useSpeakerBoost: true,
speed: 1.0,
},
2026-03-19 10:42:46 -07:00
},
},
},
},
}
```
### Microsoft primary (no API key)
``` json5
{
messages: {
tts: {
auto: "always",
provider: "microsoft",
2026-03-26 23:22:35 +00:00
providers: {
microsoft: {
enabled: true,
voice: "en-US-MichelleNeural",
lang: "en-US",
outputFormat: "audio-24khz-48kbitrate-mono-mp3",
rate: "+10%",
pitch: "-5%",
},
2026-03-19 10:42:46 -07:00
},
},
},
}
```
2026-03-27 16:24:56 +01:00
### MiniMax primary
``` json5
{
messages: {
tts: {
auto: "always",
provider: "minimax",
providers: {
minimax: {
apiKey: "minimax_api_key",
2026-03-31 10:13:08 +02:00
baseUrl: "https://api.minimax.io",
2026-03-27 16:24:56 +01:00
model: "speech-2.8-hd",
voiceId: "English_expressive_narrator",
speed: 1.0,
vol: 1.0,
pitch: 0,
},
},
},
},
}
```
2026-03-19 10:42:46 -07:00
### Disable Microsoft speech
``` json5
{
messages: {
tts: {
2026-03-26 23:22:35 +00:00
providers: {
microsoft: {
enabled: false,
},
2026-03-19 10:42:46 -07:00
},
},
},
}
```
### Custom limits + prefs path
``` json5
{
messages: {
tts: {
auto: "always",
maxTextLength: 4000,
timeoutMs: 30000,
prefsPath: "~/.openclaw/settings/tts.json",
},
},
}
```
2026-03-24 15:53:01 +08:00
### Only reply with audio after an inbound voice message
2026-03-19 10:42:46 -07:00
``` json5
{
messages: {
tts: {
auto: "inbound",
},
},
}
```
### Disable auto-summary for long replies
``` json5
{
messages: {
tts: {
auto: "always",
},
},
}
```
Then run:
```
/tts summary off
```
### Notes on fields
- `auto` : auto‑ TTS mode (`off` , `always` , `inbound` , `tagged` ).
2026-03-24 15:53:01 +08:00
- `inbound` only sends audio after an inbound voice message.
2026-03-19 10:42:46 -07:00
- `tagged` only sends audio when the reply includes `[[tts]]` tags.
- `enabled` : legacy toggle (doctor migrates this to `auto` ).
- `mode` : `"final"` (default) or `"all"` (includes tool/block replies).
2026-03-27 16:24:56 +01:00
- `provider` : speech provider id such as `"elevenlabs"` , `"microsoft"` , `"minimax"` , or `"openai"` (fallback is automatic).
2026-03-26 23:22:35 +00:00
- If `provider` is **unset ** , OpenClaw uses the first configured speech provider in registry auto-select order.
2026-03-19 10:42:46 -07:00
- Legacy `provider: "edge"` still works and is normalized to `microsoft` .
- `summaryModel` : optional cheap model for auto-summary; defaults to `agents.defaults.model.primary` .
- Accepts `provider/model` or a configured model alias.
- `modelOverrides` : allow the model to emit TTS directives (on by default).
- `allowProvider` defaults to `false` (provider switching is opt-in).
2026-03-26 23:22:35 +00:00
- `providers.<id>` : provider-owned settings keyed by speech provider id.
2026-03-30 22:05:03 -05:00
- Legacy direct provider blocks (`messages.tts.openai` , `messages.tts.elevenlabs` , `messages.tts.microsoft` , `messages.tts.edge` ) are auto-migrated to `messages.tts.providers.<id>` on load.
2026-03-19 10:42:46 -07:00
- `maxTextLength` : hard cap for TTS input (chars). `/tts audio` fails if exceeded.
- `timeoutMs` : request timeout (ms).
- `prefsPath` : override the local prefs JSON path (provider/limit/summary).
2026-03-27 16:24:56 +01:00
- `apiKey` values fall back to env vars (`ELEVENLABS_API_KEY` /`XI_API_KEY` , `MINIMAX_API_KEY` , `OPENAI_API_KEY` ).
2026-03-26 23:22:35 +00:00
- `providers.elevenlabs.baseUrl` : override ElevenLabs API base URL.
- `providers.openai.baseUrl` : override the OpenAI TTS endpoint.
- Resolution order: `messages.tts.providers.openai.baseUrl` -> `OPENAI_TTS_BASE_URL` -> `https://api.openai.com/v1`
2026-03-19 10:42:46 -07:00
- Non-default values are treated as OpenAI-compatible TTS endpoints, so custom model and voice names are accepted.
2026-03-26 23:22:35 +00:00
- `providers.elevenlabs.voiceSettings` :
2026-03-19 10:42:46 -07:00
- `stability` , `similarityBoost` , `style` : `0..1`
- `useSpeakerBoost` : `true|false`
- `speed` : `0.5..2.0` (1.0 = normal)
2026-03-26 23:22:35 +00:00
- `providers.elevenlabs.applyTextNormalization` : `auto|on|off`
- `providers.elevenlabs.languageCode` : 2-letter ISO 639-1 (e.g. `en` , `de` )
- `providers.elevenlabs.seed` : integer `0..4294967295` (best-effort determinism)
2026-03-31 10:13:08 +02:00
- `providers.minimax.baseUrl` : override MiniMax API base URL (default `https://api.minimax.io` , env: `MINIMAX_API_HOST` ).
2026-03-27 16:24:56 +01:00
- `providers.minimax.model` : TTS model (default `speech-2.8-hd` , env: `MINIMAX_TTS_MODEL` ).
- `providers.minimax.voiceId` : voice identifier (default `English_expressive_narrator` , env: `MINIMAX_TTS_VOICE_ID` ).
- `providers.minimax.speed` : playback speed `0.5..2.0` (default 1.0).
- `providers.minimax.vol` : volume `(0, 10]` (default 1.0; must be greater than 0).
- `providers.minimax.pitch` : pitch shift `-12..12` (default 0).
2026-03-26 23:22:35 +00:00
- `providers.microsoft.enabled` : allow Microsoft speech usage (default `true` ; no API key).
- `providers.microsoft.voice` : Microsoft neural voice name (e.g. `en-US-MichelleNeural` ).
- `providers.microsoft.lang` : language code (e.g. `en-US` ).
- `providers.microsoft.outputFormat` : Microsoft output format (e.g. `audio-24khz-48kbitrate-mono-mp3` ).
2026-03-19 10:42:46 -07:00
- See Microsoft Speech output formats for valid values; not all formats are supported by the bundled Edge-backed transport.
2026-03-26 23:22:35 +00:00
- `providers.microsoft.rate` / `providers.microsoft.pitch` / `providers.microsoft.volume` : percent strings (e.g. `+10%` , `-5%` ).
- `providers.microsoft.saveSubtitles` : write JSON subtitles alongside the audio file.
- `providers.microsoft.proxy` : proxy URL for Microsoft speech requests.
- `providers.microsoft.timeoutMs` : request timeout override (ms).
2026-03-19 10:42:46 -07:00
- `edge.*` : legacy alias for the same Microsoft settings.
## Model-driven overrides (default on)
By default, the model **can ** emit TTS directives for a single reply.
When `messages.tts.auto` is `tagged` , these directives are required to trigger audio.
When enabled, the model can emit `[[tts:...]]` directives to override the voice
for a single reply, plus an optional `[[tts:text]]...[[/tts:text]]` block to
provide expressive tags (laughter, singing cues, etc) that should only appear in
the audio.
`provider=...` directives are ignored unless `modelOverrides.allowProvider: true` .
Example reply payload:
```
Here you go.
[[tts:voiceId=pMsXgVXv3BLzUgSXRplE model=eleven_v3 speed=1.1]]
[[tts:text]](laughs) Read the song once more.[[/tts:text]]
```
Available directive keys (when enabled):
2026-03-27 16:24:56 +01:00
- `provider` (registered speech provider id, for example `openai` , `elevenlabs` , `minimax` , or `microsoft` ; requires `allowProvider: true` )
- `voice` (OpenAI voice) or `voiceId` (ElevenLabs / MiniMax)
- `model` (OpenAI TTS model, ElevenLabs model id, or MiniMax model)
2026-03-19 10:42:46 -07:00
- `stability` , `similarityBoost` , `style` , `speed` , `useSpeakerBoost`
2026-03-27 16:24:56 +01:00
- `vol` / `volume` (MiniMax volume, 0-10)
- `pitch` (MiniMax pitch, -12 to 12)
2026-03-19 10:42:46 -07:00
- `applyTextNormalization` (`auto|on|off` )
- `languageCode` (ISO 639-1)
- `seed`
Disable all model overrides:
``` json5
{
messages: {
tts: {
modelOverrides: {
enabled: false,
},
},
},
}
```
Optional allowlist (enable provider switching while keeping other knobs configurable):
``` json5
{
messages: {
tts: {
modelOverrides: {
enabled: true,
allowProvider: true,
allowSeed: false,
},
},
},
}
```
## Per-user preferences
Slash commands write local overrides to `prefsPath` (default:
`~/.openclaw/settings/tts.json` , override with `OPENCLAW_TTS_PREFS` or
`messages.tts.prefsPath` ).
Stored fields:
- `enabled`
- `provider`
- `maxLength` (summary threshold; default 1500 chars)
- `summarize` (default `true` )
These override `messages.tts.*` for that host.
## Output formats (fixed)
2026-03-24 15:53:01 +08:00
- **Feishu / Matrix / Telegram / WhatsApp**: Opus voice message (`opus_48000_64` from ElevenLabs, `opus` from OpenAI).
- 48kHz / 64kbps is a good voice message tradeoff.
2026-03-19 10:42:46 -07:00
- **Other channels**: MP3 (`mp3_44100_128` from ElevenLabs, `mp3` from OpenAI).
- 44.1kHz / 128kbps is the default balance for speech clarity.
2026-03-27 16:24:56 +01:00
- **MiniMax**: MP3 (`speech-2.8-hd` model, 32kHz sample rate). Voice-note format not natively supported; use OpenAI or ElevenLabs for guaranteed Opus voice messages.
2026-03-19 10:42:46 -07:00
- **Microsoft**: uses `microsoft.outputFormat` (default `audio-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 `sendVoice` accepts OGG/MP3/M4A; use OpenAI/ElevenLabs if you need
2026-03-24 15:53:01 +08:00
guaranteed Opus voice messages.
2026-03-19 10:42:46 -07:00
- If the configured Microsoft output format fails, OpenClaw retries with MP3.
2026-03-24 15:53:01 +08:00
OpenAI/ElevenLabs output formats are fixed per channel (see above).
2026-03-19 10:42:46 -07:00
## Auto-TTS behavior
When enabled, OpenClaw:
- skips TTS if the reply already contains media or a `MEDIA:` directive.
- skips very short replies (< 10 chars).
- summarizes long replies when enabled using `agents.defaults.model.primary` (or `summaryModel` ).
- attaches the generated audio to the reply.
If the reply exceeds `maxLength` and summary is off (or no API key for the
summary model), audio
is skipped and the normal text reply is sent.
## Flow diagram
```
Reply -> TTS enabled?
no -> send text
yes -> has media / MEDIA: / short?
yes -> send text
no -> length > limit?
no -> TTS -> attach audio
yes -> summary enabled?
no -> send text
yes -> summarize (summaryModel or agents.defaults.model.primary)
-> TTS -> attach audio
```
## Slash command usage
There is a single command: `/tts` .
See [Slash commands ](/tools/slash-commands ) for enablement details.
Discord note: `/tts` is a built-in Discord command, so OpenClaw registers
`/voice` as the native command there. Text `/tts ...` still works.
```
/tts off
/tts always
/tts inbound
/tts tagged
/tts status
/tts provider openai
/tts limit 2000
/tts summary off
/tts audio Hello from OpenClaw
```
Notes:
- Commands require an authorized sender (allowlist/owner rules still apply).
- `commands.text` or native command registration must be enabled.
- `off|always|inbound|tagged` are per‑ session toggles (`/tts on` is an alias for `/tts always` ).
- `limit` and `summary` are stored in local prefs, not the main config.
- `/tts audio` generates a one-off audio reply (does not toggle TTS on).
2026-03-30 22:05:03 -05:00
- `/tts status` includes fallback visibility for the latest attempt:
- success fallback: `Fallback: <primary> -> <used>` plus `Attempts: ...`
- failure: `Error: ...` plus `Attempts: ...`
2026-03-30 22:55:28 -05:00
- detailed diagnostics: `Attempt details: provider:outcome(reasonCode) latency`
- OpenAI and ElevenLabs API failures now include parsed provider error detail and request id (when returned by the provider), which is surfaced in TTS errors/logs.
2026-03-19 10:42:46 -07:00
## Agent tool
2026-03-22 00:49:18 -07:00
The `tts` tool converts text to speech and returns an audio attachment for
2026-03-24 15:53:01 +08:00
reply delivery. When the channel is Feishu, Matrix, Telegram, or WhatsApp,
the audio is delivered as a voice message rather than a file attachment.
2026-03-19 10:42:46 -07:00
## Gateway RPC
Gateway methods:
- `tts.status`
- `tts.enable`
- `tts.disable`
- `tts.convert`
- `tts.setProvider`
- `tts.providers`