Text-to-Speech for Outbound Replies: Providers, Config, and Commands

This page covers OpenClaw's text-to-speech system for outbound replies, including 14 supported providers, per-channel output, and slash commands. It is intended for users configuring TTS for voice replies across platforms.

Read this when

  • Enabling text-to-speech for replies
  • Configuring a TTS provider, fallback chain, or persona
  • Using /tts commands or directives

OpenClaw turns outbound replies into spoken audio using 14 different speech providers: native voice messages on Feishu, Matrix, Telegram, and WhatsApp; audio file attachments for all other platforms; and PCM/Ulaw streams for telephony and Talk.

TTS handles the speech generation side of Talk's stt-tts mode (talk.speak uses this same synthesis pipeline). Provider-native realtime Talk sessions produce speech directly within the realtime provider; transcription sessions never generate an assistant voice reply.

Quick start

Pick a provider

For hosted solutions, OpenAI and ElevenLabs offer the greatest reliability. Microsoft and Local CLI function without requiring an API key. The full provider list is available in the provider matrix.

Set the API key

Set the environment variable for your chosen provider (for example OPENAI_API_KEY, ELEVENLABS_API_KEY). Microsoft and Local CLI do not need a key.

Enable in config

Configure tts.auto: "always" and tts.provider:

{
  tts: {
    auto: "always",
    provider: "elevenlabs",
  },
}

Try it in chat

/tts status displays the current status. /tts audio Hello from OpenClaw triggers a single audio reply.

Note

Auto-TTS is disabled by default. When tts.provider is not set, OpenClaw selects the first configured provider based on the registry auto-select order. The built-in tts agent tool only responds to explicit intent: regular chat remains text-based unless the user requests audio, uses /tts, or activates Auto-TTS or directive speech.

Supported providers

ProviderAuthNotes
Azure SpeechAZURE_SPEECH_KEY + AZURE_SPEECH_REGION (also AZURE_SPEECH_API_KEY, SPEECH_KEY, SPEECH_REGION)Native Ogg/Opus voice-note output and telephony.
DeepInfraDEEPINFRA_API_KEYOpenAI-compatible TTS. Defaults to hexgrad/Kokoro-82M.
ElevenLabsELEVENLABS_API_KEY or XI_API_KEYVoice cloning, multilingual, deterministic via seed; streamed for Discord voice playback.
Google GeminiGEMINI_API_KEY or GOOGLE_API_KEYGemini API batch TTS; persona-aware via promptTemplate: "audio-profile-v1".
GradiumGRADIUM_API_KEYVoice-note and telephony output.
InworldINWORLD_API_KEYStreaming TTS API. Native Opus voice-note and PCM telephony.
Local CLInoneRuns a configured local TTS command.
MicrosoftnonePublic Edge neural TTS via node-edge-tts. Best-effort, no SLA.
MiniMaxMINIMAX_API_KEY (or Token Plan: MINIMAX_OAUTH_TOKEN, MINIMAX_CODE_PLAN_KEY, MINIMAX_CODING_API_KEY)T2A v2 API. Defaults to speech-2.8-hd.
OpenAIOPENAI_API_KEYAlso used for auto-summary; supports persona instructions.
OpenRouterOPENROUTER_API_KEY (can reuse models.providers.openrouter.apiKey)Default model hexgrad/kokoro-82m.
VolcengineVOLCENGINE_TTS_API_KEY or BYTEPLUS_SEED_SPEECH_API_KEY (legacy AppID/token: VOLCENGINE_TTS_APPID/_TOKEN)BytePlus Seed Speech HTTP API.
VydraVYDRA_API_KEYShared image, video, and speech provider.
xAIXAI_API_KEYxAI batch TTS. Native Opus voice-note is not supported.
Xiaomi MiMoXIAOMI_API_KEYMiMo TTS through Xiaomi chat completions.

When multiple providers are configured, the selected one takes priority and the rest act as fallbacks. Auto-summary uses summaryModel (or agents.defaults.model.primary), so that provider must also have valid authentication if summaries remain enabled.

Caution

The included Microsoft provider relies on Microsoft Edge's cloud-based neural TTS endpoint at node-edge-tts. This is a publicly accessible web service with no documented SLA or rate limits, so consider it best-effort only. The older provider identifier edge gets normalized to microsoft, and openclaw doctor --fix updates saved configuration; any new setup should use microsoft going forward.

Configuration

TTS configuration is stored under tts inside ~/.openclaw/openclaw.json. Choose a preset and customize the provider section. The speakerVoice and speakerVoiceId fields listed here are the standard ones; each provider's original voice, voiceId, and voiceName field names remain usable as backward-compatible alternatives.

Azure Speech

{
  tts: {
    auto: "always",
    provider: "azure-speech",
    providers: {
      "azure-speech": {
        apiKey: "${AZURE_SPEECH_KEY}",
        region: "eastus",
        speakerVoice: "en-US-JennyNeural",
        lang: "en-US",
        outputFormat: "audio-24khz-48kbitrate-mono-mp3",
        voiceNoteOutputFormat: "ogg-24khz-16bit-mono-opus",
      },
    },
  },
}

ElevenLabs

{
  tts: {
    auto: "always",
    provider: "elevenlabs",
    providers: {
      elevenlabs: {
        apiKey: "${ELEVENLABS_API_KEY}",
        model: "eleven_multilingual_v2",
        speakerVoiceId: "EXAVITQu4vr4xnSDxMaL",
      },
    },
  },
}

Google Gemini

{
  tts: {
    auto: "always",
    provider: "google",
    providers: {
      google: {
        apiKey: "${GEMINI_API_KEY}",
        model: "gemini-3.1-flash-tts-preview",
        speakerVoice: "Kore",
        // Optional natural-language style prompts:
        // audioProfile: "Speak in a calm, podcast-host tone.",
        // speakerName: "Alex",
      },
    },
  },
}

Gradium

{
  tts: {
    auto: "always",
    provider: "gradium",
    providers: {
      gradium: {
        apiKey: "${GRADIUM_API_KEY}",
        speakerVoiceId: "YTpq7expH9539ERJ",
      },
    },
  },
}

Inworld

{
  tts: {
    auto: "always",
    provider: "inworld",
    providers: {
      inworld: {
        apiKey: "${INWORLD_API_KEY}",
        modelId: "inworld-tts-1.5-max",
        speakerVoiceId: "Sarah",
        temperature: 0.7,
      },
    },
  },
}

Local CLI

{
  tts: {
    auto: "always",
    provider: "tts-local-cli",
    providers: {
      "tts-local-cli": {
        command: "say",
        args: ["-o", "{{OutputPath}}", "{{Text}}"],
        outputFormat: "wav",
        timeoutMs: 120000,
      },
    },
  },
}

Microsoft (no key)

{
  tts: {
    auto: "always",
    provider: "microsoft",
    providers: {
      microsoft: {
        enabled: true,
        speakerVoice: "en-US-MichelleNeural",
        lang: "en-US",
        outputFormat: "audio-24khz-48kbitrate-mono-mp3",
        rate: "+0%",
        pitch: "+0%",
      },
    },
  },
}

MiniMax

{
  tts: {
    auto: "always",
    provider: "minimax",
    providers: {
      minimax: {
        apiKey: "${MINIMAX_API_KEY}",
        model: "speech-2.8-hd",
        speakerVoiceId: "English_expressive_narrator",
        speed: 1.0,
        vol: 1.0,
        pitch: 0,
      },
    },
  },
}

OpenAI + ElevenLabs

{
  tts: {
    auto: "always",
    provider: "openai",
    summaryModel: "openai/gpt-4.1-mini",
    modelOverrides: { enabled: true },
    providers: {
      openai: {
        apiKey: "${OPENAI_API_KEY}",
        model: "gpt-4o-mini-tts",
        speakerVoice: "alloy",
      },
      elevenlabs: {
        apiKey: "${ELEVENLABS_API_KEY}",
        model: "eleven_multilingual_v2",
        speakerVoiceId: "EXAVITQu4vr4xnSDxMaL",
        voiceSettings: { stability: 0.5, similarityBoost: 0.75, style: 0.0, useSpeakerBoost: true, speed: 1.0 },
        applyTextNormalization: "auto",
        languageCode: "en",
      },
    },
  },
}

OpenRouter

{
  tts: {
    auto: "always",
    provider: "openrouter",
    providers: {
      openrouter: {
        apiKey: "${OPENROUTER_API_KEY}",
        model: "hexgrad/kokoro-82m",
        speakerVoice: "af_alloy",
        responseFormat: "mp3",
      },
    },
  },
}

Volcengine

{
  tts: {
    auto: "always",
    provider: "volcengine",
    providers: {
      volcengine: {
        apiKey: "${VOLCENGINE_TTS_API_KEY}",
        resourceId: "seed-tts-1.0",
        speakerVoice: "en_female_anna_mars_bigtts",
      },
    },
  },
}

xAI

{
  tts: {
    auto: "always",
    provider: "xai",
    providers: {
      xai: {
        apiKey: "${XAI_API_KEY}",
        speakerVoiceId: "eve",
        language: "en",
        responseFormat: "mp3",
      },
    },
  },
}

Xiaomi MiMo

{
  tts: {
    auto: "always",
    provider: "xiaomi",
    providers: {
      xiaomi: {
        apiKey: "${XIAOMI_API_KEY}",
        model: "mimo-v2.5-tts",
        speakerVoice: "mimo_default",
        format: "mp3",
      },
    },
  },
}

For Xiaomi mimo-v2.5-tts-voicedesign, leave out speakerVoice and assign style to the voice design prompt. OpenClaw forwards that prompt as the TTS user message and does not send audio.voice for the voicedesign model.

Per-agent voice overrides

Apply agents.entries.*.tts when a single agent needs to talk using a different provider, voice, model, persona, or auto-TTS mode. The agent block deep-merges on top of tts, so provider credentials can remain in the global provider configuration:

{
  tts: {
    auto: "always",
    provider: "elevenlabs",
    providers: {
      elevenlabs: { apiKey: "${ELEVENLABS_API_KEY}", model: "eleven_multilingual_v2" },
    },
  },
  agents: {
    list: [
      {
        id: "reader",
        tts: {
          providers: {
            elevenlabs: { speakerVoiceId: "EXAVITQu4vr4xnSDxMaL" },
          },
        },
      },
    ],
  },
}

To lock a per-agent persona, configure agents.entries.*.tts.persona together with the provider settings. This overrides the global tts.persona for just that agent.

Priority order for automatic replies, /tts audio, /tts status, and the tts agent tool:

  1. tts
  2. active agents.entries.*.tts
  3. channel override, provided the channel supports channels.<channel>.tts
  4. account override, when the channel sends channels.<channel>.accounts.<id>.tts
  5. local /tts preferences on this host
  6. inline [[tts:...]] directives if model overrides are turned on

Channel and account overrides follow the same structure as tts and deep-merge over earlier layers. This lets shared provider credentials stay in tts while a channel or bot account changes only the speaker voice, model, persona, or auto mode:

{
  tts: {
    provider: "openai",
    providers: {
      openai: { apiKey: "${OPENAI_API_KEY}", model: "gpt-4o-mini-tts" },
    },
  },
  channels: {
    feishu: {
      accounts: {
        english: {
          tts: {
            providers: {
              openai: { speakerVoice: "shimmer" },
            },
          },
        },
      },
    },
  },
}

Personas

A persona represents a consistent spoken identity that can be assigned deterministically across different providers. It may favor one provider, define provider-neutral prompt intent, and include provider-specific mappings for voices, models, prompt templates, seeds, and voice parameters.

Minimal persona

{
  tts: {
    auto: "always",
    persona: "narrator",
    personas: {
      narrator: {
        label: "Narrator",
        provider: "elevenlabs",
        providers: {
          elevenlabs: {
            speakerVoiceId: "EXAVITQu4vr4xnSDxMaL",
            modelId: "eleven_multilingual_v2",
          },
        },
      },
    },
  },
}

Full persona (provider-specific shaping)

{
  tts: {
    auto: "always",
    persona: "alfred",
    personas: {
      alfred: {
        label: "Alfred",
        description: "Dry, warm British butler narrator.",
        provider: "google",
        fallbackPolicy: "preserve-persona",
        providers: {
          google: {
            model: "gemini-3.1-flash-tts-preview",
            speakerVoice: "Algieba",
            promptTemplate: "audio-profile-v1",
          },
          openai: { model: "gpt-4o-mini-tts", speakerVoice: "cedar" },
          elevenlabs: {
            speakerVoiceId: "voice_id",
            modelId: "eleven_multilingual_v2",
            seed: 42,
            voiceSettings: {
              stability: 0.65,
              similarityBoost: 0.8,
              style: 0.25,
              useSpeakerBoost: true,
              speed: 0.95,
            },
          },
        },
      },
    },
  },
}

Persona resolution

The active persona is chosen deterministically:

  1. /tts persona <id> local preference, if set.
  2. tts.persona, if set.
  3. No persona.

Provider selection follows an explicit-first approach:

  1. Direct overrides (CLI, gateway, Talk, allowed TTS directives).
  2. /tts provider <id> local preference.
  3. Active persona's provider.
  4. tts.provider.
  5. Registry auto-select.

When trying each provider, OpenClaw applies configurations in this sequence:

  1. tts.providers.<id>
  2. tts.personas.<persona>.providers.<id>
  3. Trusted request overrides
  4. Allowed model-emitted TTS directive overrides

Custom persona shaping

The provider-neutral personas.<id>.prompt.* config has been deprecated. Doctor removes those fields and redirects them to the speech-provider seam. Place built-in provider settings inside personas.<id>.providers.<provider> (for instance Google personaPrompt or OpenAI instructions). For custom behavior, write a speech provider plugin using prepareSynthesis(ctx) and hand back modified text, provider config, or overrides before synthesize() executes. This keeps expressive prompt construction in provider code where request semantics are understood.

Fallback policy

fallbackPolicy decides what happens when a persona has no binding for the provider being tried:

PolicyBehavior
preserve-personaDefault. Provider-neutral prompt fields remain accessible; the provider can use them or disregard them.
provider-defaultsThat persona is left out of prompt preparation for this attempt; the provider uses its neutral defaults while fallback to other providers proceeds.
failSkip that provider attempt with reasonCode: "not_configured" and personaBinding: "missing". Fallback providers are still attempted.

The entire TTS request fails only when every attempted provider is skipped or fails.

Talk session provider selection applies per session. A Talk client should pick provider ids, model ids, voice ids, and locales from talk.catalog and send them through the Talk session or handoff request. Opening a voice session should not change 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:

Here you go.

[[tts:speakerVoiceId=pMsXgVXv3BLzUgSXRplE model=eleven_v3 speed=1.1]]
[[tts:text]](laughs) Read the song once more.[[/tts:text]]

When 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; requires allowProvider: true)
  • speakerVoice / speakerVoiceId (legacy aliases: voice, voiceName, voice_name, google_voice, voiceId)
  • model / google_model
  • stability, similarityBoost, style, speed, useSpeakerBoost
  • vol / 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

Disable model overrides entirely:

{ messages: { tts: { modelOverrides: { enabled: false } } } }

Allow provider switching while keeping other knobs configurable:

{ messages: { tts: { modelOverrides: { enabled: true, allowProvider: true, allowSeed: false } } } }

Slash commands

A single command is available as /tts. On Discord, OpenClaw also registers /voice because /tts is a built-in Discord command. The text version /tts ... remains functional.

/tts off | on | status
/tts chat on | off | default
/tts latest
/tts provider <id>
/tts persona <id> | off
/tts limit <chars>
/tts summary off
/tts audio <text>

Note

Commands need an authorized sender (subject to allowlist/owner rules) and either commands.text or native command registration must be turned on.

Behavior notes:

  • /tts on stores the local TTS preference in always; /tts off stores it in off.
  • /tts chat on|off|default writes a session-scoped auto-TTS override for the current conversation.
  • /tts persona <id> stores the local persona preference; /tts persona off removes it.
  • /tts latest retrieves the most recent assistant reply from the current session transcript and plays it as audio once. Only a hash of that reply is saved on the session entry to prevent duplicate voice delivery.
  • /tts audio creates a one-off audio reply without toggling TTS on or off.
  • /tts limit <chars> accepts values from 100 to 4096 (4096 is the Telegram caption/message maximum); anything outside that range is rejected.
  • limit and summary reside in local prefs, not the main configuration.
  • /tts status provides fallback diagnostics for the most recent attempt: Fallback: <primary> -> <used>, Attempts: ..., and per-attempt details (provider:outcome(reasonCode) latency).
  • /status displays the active TTS mode along with the 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 deliberately use separate preference stores.

Stored fieldEffect
autoLocal auto-TTS override (always, off, …)
providerLocal primary provider override
personaLocal persona override
maxLengthSummary/truncation threshold (default 1500 chars, /tts limit range 100, 4096)
summarizeSummary toggle (default true)

These override the effective configuration from tts plus the active agents.entries.*.tts block for that host.

Output formats

TTS voice delivery depends on channel capabilities. Channel plugins indicate whether voice-style TTS should request a native voice-note target from providers or keep standard audio-file synthesis, and whether the channel transcodes non-native output before delivery.

TargetFormat
Feishu / Matrix / Telegram / WhatsAppVoice-note replies prefer Opus (opus_48000_64 from ElevenLabs, opus from OpenAI). 48 kHz / 64 kbps balances clarity and size.
Other channelsMP3 (mp3_44100_128 from ElevenLabs, mp3 from OpenAI). 44.1 kHz / 128 kbps is the default balance for speech.
Talk / telephonyProvider-native PCM (Inworld 22050 Hz, Google 24 kHz), or ulaw_8000 from Gradium for telephony.

Per-provider notes:

  • Feishu / WhatsApp transcoding: When a voice note reply comes in as MP3, WebM, WAV, M4A, or another probable audio format, the channel plugin converts it to 48 kHz Ogg/Opus using ffmpeg (libopus, 64 kbps) before dispatching the native voice message. WhatsApp transmits the result through the Baileys audio payload with ptt: true and audio/ogg; codecs=opus. If transcoding fails: Feishu catches the error and reverts to sending the original file as a plain attachment; WhatsApp has no fallback, so the send itself fails instead of delivering an incompatible PTT payload.
  • MiniMax: MP3 (speech-2.8-hd model, 32 kHz sample rate) for standard audio attachments; converted to 48 kHz Opus with ffmpeg for channel-advertised voice note targets.
  • Xiaomi MiMo: MP3 by default, or WAV when configured; converted to 48 kHz Opus with ffmpeg for channel-advertised voice note targets.
  • Local CLI: uses the configured outputFormat. Voice note targets are transformed to Ogg/Opus and telephony output is converted to raw 16 kHz mono PCM with ffmpeg.
  • Google Gemini: returns raw 24 kHz PCM. OpenClaw packages it as WAV for audio attachments, converts 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_8000 at 8 kHz for telephony.
  • Inworld: MP3 for normal audio attachments, native OGG_OPUS for voice note targets, and raw PCM at 22050 Hz for Talk/telephony.
  • xAI: MP3 by default; audio file synthesis may use mp3, wav, pcm, mulaw, or alaw for both buffered and streaming output. Voice note targets use MP3 for streaming and buffered fallback because xAI's pcm, mulaw, and alaw outputs are headerless raw audio. Buffered synthesis uses xAI's batch REST /v1/tts endpoint; textToSpeechStream uses native wss://api.x.ai/v1/tts. This is not the realtime voice contract. Native Opus voice note format is not supported.
  • 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 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-CN locale) if the reply text is CJK-dominant.

OpenAI and ElevenLabs output formats are fixed per channel as listed above.

Auto-TTS behavior

When tts.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 (or agents.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.

If the reply exceeds maxLength, OpenClaw never skips audio outright:

  • Summary on (default) and a summary model is available: summarizes the text to roughly maxLength chars, then synthesizes the summary.
  • Summary off, summarization fails, or no API key is available for the summary model: truncates the text to maxLength chars and synthesizes the truncated text.
Reply -> TTS enabled?
  no  -> send text
  yes -> has media / short?
          yes -> send text
          no  -> length > limit?
                   no  -> TTS -> attach audio
                   yes -> summary enabled and available?
                            no  -> truncate -> TTS -> attach audio
                            yes -> summarize -> TTS -> attach audio

Field reference

Top-level tts.*

  • auto (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.

  • enabled (boolean), Legacy toggle. openclaw doctor --fix migrates this to auto.

  • mode (final" | "all, default: final), "all" includes tool/block replies in addition to final replies.

  • provider (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.

  • persona (string), Active persona id from personas. Normalized to lowercase.

  • true, " type="object"> A consistent vocal identity. Contains these fields: label, description, provider, fallbackPolicy, prompt, providers.<provider>. Refer to Personas.

  • summaryModel (string), An inexpensive model intended for auto-summarization; agents.defaults.model.primary is the default. Either provider/model or a configured model alias is accepted.

  • modelOverrides (object), Permits the model to issue TTS instructions. enabled has a default of true; allowProvider defaults to false.

  • true, " type="object"> Provider-specific settings, organized by speech provider identifier. Legacy direct blocks (tts.openai, .elevenlabs, .microsoft, .edge) are converted by openclaw doctor --fix; only tts.providers.<id> should be committed.

  • maxTextLength (number, default: 4096), The maximum number of characters allowed for TTS input. Requests exceeding this limit cause /tts audio, tts.convert, and tts.speak to fail.

  • timeoutMs (number, default: 30000), Timeout for requests, expressed in milliseconds. A per-call timeoutMs (agent tool, gateway) takes precedence when provided; otherwise, an explicitly configured tts.timeoutMs overrides any plugin-defined provider default.

Provider apiKey fields may be either plain strings or SecretRefs. During a cold Gateway startup, if a TTS SecretRef is unavailable, the built-in TTS capability is marked as configured-unavailable rather than halting the Gateway. tts.speak then returns UNAVAILABLE with reason SECRET_SURFACE_UNAVAILABLE, and no provider request is dispatched. The status and doctor list show the degraded TTS owner along with its configuration paths. The explicit refs stay in the runtime snapshot, preventing environment or profile credentials from silently selecting a different account. Reloads and config-write preflight checks apply an owner-aware degradation policy: an unchanged eligible TTS owner may retain its last-known-good credentials as stale, whereas a new or changed failure becomes cold without blocking healthy owners. Structurally invalid refs and resolved values still cause startup to fail or the update to be rejected.

Azure Speech

  • apiKey (string), Environment variable: AZURE_SPEECH_KEY, AZURE_SPEECH_API_KEY, or SPEECH_KEY.

  • region (string), The Azure Speech region (for example, eastus). Environment variable: AZURE_SPEECH_REGION or SPEECH_REGION.

  • endpoint (string), An optional override for the Azure Speech endpoint (alias baseUrl).

  • speakerVoice (string), The ShortName of the Azure voice. Default is en-US-JennyNeural. Legacy alias: voice.

  • lang (string), The SSML language code. Default is en-US.

  • outputFormat (string), The Azure X-Microsoft-OutputFormat for standard audio output. Default is audio-24khz-48kbitrate-mono-mp3.

  • voiceNoteOutputFormat (string), The Azure X-Microsoft-OutputFormat for voice-note output. Default is ogg-24khz-16bit-mono-opus.

    ElevenLabs

  • apiKey (string), Falls back to ELEVENLABS_API_KEY or XI_API_KEY.

  • model (string), Identifies the model. Falls back to eleven_multilingual_v2. Older identifiers eleven_turbo_v2_5 and eleven_turbo_v2 get mapped to the corresponding flash model automatically.

  • speakerVoiceId (string), The ElevenLass voice identifier. Uses pMsXgVXv3BLzUgSXRplE by default. An older name for this is voiceId.

  • voiceSettings (object), Contains stability, similarityBoost, style (each one is 0..1, with defaults 0.5, 0.75, 0), useSpeakerBoost (type true|false, default true), and speed (type 0.5..2.0, default 1.0).

  • applyTextNormalization (auto" | "on" | "off), Controls how text normalization is applied.

  • languageCode (string), A two-letter code from ISO 639-1, for instance en or de.

  • seed (number), An integer 0..4294967295 used to attempt deterministic behavior.

  • baseUrl (string), Lets you replace the default ElevenLabs API base URL.

    Google Gemini

  • apiKey (string), Tries GEMINI_API_KEY and GOOGLE_API_KEY as fallbacks. When not provided, TTS may reuse models.providers.google.apiKey before checking environment variables.

  • model (string), The Gemini TTS model to use. Defaults to gemini-3.1-flash-tts-preview.

  • speakerVoice (string), A prebuilt voice name from Gemini. Default is Kore. Older names include voiceName and voice.

  • audioProfile (string), A style prompt in natural language that gets inserted before the spoken content.

  • speakerName (string), An optional speaker tag placed before the spoken text when your prompt references a specific speaker.

  • promptTemplate (audio-profile-v1), Set to audio-profile-v1 to enclose active persona prompt fields inside a fixed Gemini TTS prompt template.

  • personaPrompt (string), Extra persona prompt text specific to Google, appended to the Director's Notes section of the template.

  • baseUrl (string), Only https://generativelanguage.googleapis.com is allowed here.

    Gradium

  • apiKey (string), Read from the environment variable GRADIUM_API_KEY.

  • baseUrl (string), A Gradium API URL over HTTPS on api.gradium.ai. Defaults to https://api.gradium.ai.

  • speakerVoiceId (string), Defaults to Emma (YTpq7expH9539ERJ). The older name voiceId still works.

    Inworld

    Inworld primary

  • apiKey (string), Sourced from the environment variable INWORLD_API_KEY.

  • baseUrl (string), Falls back to https://api.inworld.ai.

  • modelId (string), Falls back to inworld-tts-1.5-max. Options include inworld-tts-1.5-mini, inworld-tts-1-max, and inworld-tts-1.

  • speakerVoiceId (string), Falls back to Sarah. Previously known as voiceId.

  • temperature (number), Sampling temperature 0..2 (0 is excluded).

    Local CLI (tts-local-cli)

  • command (string), Path to a local binary or a command string used for CLI-based TTS.

  • args (string[]), Arguments passed to the command. Accepts {{Text}}, {{OutputPath}}, {{OutputDir}}, and {{OutputBase}} as placeholders.

  • outputFormat (mp3" | "opus" | "wav), The audio format the CLI is expected to output. When attaching audio, mp3 is the default.

  • timeoutMs (number), How long (in milliseconds) to wait before the command times out. Defaults to 120000.

  • cwd (string), An optional directory to run the command from.

  • env (true), ">Optional environment variables that override the command's environment.

    Any audio produced by the command's stdout, or converted from it, must stay under 50 MiB. Diagnostic stderr output is capped at 1 MiB. If either limit is exceeded, OpenClaw kills the command and reports a synthesis failure.

    Microsoft (no API key)

  • enabled (boolean, default: true), Whether to allow speech generation through Microsoft.

  • **speakerVoice (string), Name of a Microsoft neural voice (for instance, en-US-MichelleNeural). The legacy name is voice. When the default English voice is active and the reply text is mostly CJK, OpenClaw automatically switches to zh-CN-XiaoxiaoNeural.

  • lang (string), Language identifier (for example, en-US).

  • outputFormat (string), Output format used by Microsoft. Defaults to audio-24khz-48kbitrate-mono-mp3. The bundled Edge transport does not support every format.

  • rate / pitch / volume (string), Percent strings, such as +10% or -5%.

  • saveSubtitles (boolean), Whether to write JSON subtitle files alongside the audio output.

  • proxy (string), Proxy address for Microsoft speech API calls.

  • timeoutMs (number), Request timeout override, in milliseconds.

  • edge.* (object), Legacy name. Run openclaw doctor --fix to migrate stored configuration to providers.microsoft.

    MiniMax

  • apiKey (string), Falls back to MINIMAX_API_KEY. Token Plan authentication uses MINIMAX_OAUTH_TOKEN, MINIMAX_CODE_PLAN_KEY, or MINIMAX_CODING_API_KEY.

  • baseUrl (string), Defaults to https://api.minimax.io. Can be set via the MINIMAX_API_HOST environment variable.

  • model (string), Defaults to speech-2.8-hd. Can be set via the MINIMAX_TTS_MODEL environment variable.

  • speakerVoiceId (string), Defaults to English_expressive_narrator. Can be set via the MINIMAX_TTS_VOICE_ID environment variable. Previously known as voiceId.

  • speed (number), 0.5..2.0. The default is 1.0.

  • vol (number), (0, 10]. Falls back to 1.0 by default.

  • pitch (number), An integer -12..12. Defaults to 0. Any fractional component gets removed before the request is sent.

    OpenAI

  • apiKey (string), If not provided, OPENAI_API_KEY is used instead.

  • model (string), The model identifier for the OpenAI TTS service. Defaults to gpt-4o-mini-tts.

  • speakerVoice (string), The name of the voice to use (for instance, alloy or cedar). Default is coral. The older name for this setting is voice.

  • instructions (string), Directly sets the OpenAI instructions field. When this is configured, persona prompt fields are not mapped automatically.

  • extraBody / extra_body (true), ">Additional JSON fields inserted into /audio/speech request bodies after the auto-generated OpenAI TTS fields. This is useful for OpenAI-compatible endpoints such as Kokoro that need provider-specific keys like lang. Unsafe prototype keys are ignored.

  • baseUrl (string), Replaces the default OpenAI TTS endpoint. The system checks this config first, then OPENAI_TTS_BASE_URL, then https://api.openai.com/v1. Non-default values are handled as OpenAI-compatible TTS endpoints, so custom model and voice names are allowed, and speed no longer enforces the 0.25..4.0 range check.

    OpenRouter

  • apiKey (string), Environment variable: OPENROUTER_API_KEY. Can also reuse models.providers.openrouter.apiKey.

  • baseUrl (string), Default is https://openrouter.ai/api/v1. The older https://openrouter.ai/v1 value gets normalized.

  • model (string), Defaults to hexgrad/kokoro-82m. Also known as modelId.

  • speakerVoice (string), Default is af_alloy. Older names for this setting: voice and voiceId.

  • responseFormat (mp3" | "pcm), Defaults to mp3.

  • speed (number), A speed adjustment that is native to the provider.

    Volcengine (BytePlus Seed Speech)

  • apiKey (string), Environment variable: VOLCENGINE_TTS_API_KEY or BYTEPLUS_SEED_SPEECH_API_KEY.

  • resourceId (string), Default is seed-tts-1.0. Environment variable: VOLCENGINE_TTS_RESOURCE_ID. When your project has a TTS 2.0 entitlement, use seed-tts-2.0.

  • appKey (string), The header for the app key. Default is aGjiRDfUWi. Environment variable: VOLCENGINE_TTS_APP_KEY.

  • baseUrl (string), Replaces the Seed Speech TTS HTTP endpoint. Environment variable: VOLCENGINE_TTS_BASE_URL.

  • speakerVoice (string), Determines the voice type. Defaults to en_female_anna_mars_bigtts. Can be set via the VOLCENGINE_TTS_VOICE environment variable. Previously known as voice.

  • speedRatio (number), A speed multiplier specific to the provider, with a value of 0.2..3.

  • emotion (string), An emotion tag defined by the provider.

  • appId / token / cluster (string), Fields inherited from the older Volcengine Speech Console. Configured through environment variables VOLCENGINE_TTS_APPID, VOLCENGINE_TTS_TOKEN, and VOLCENGINE_TTS_CLUSTER (the last defaults to volcano_tts).

xAI

  • apiKey (string), Set via the XAI_API_KEY environment variable.

  • baseUrl (string), Defaults to https://api.x.ai/v1. Configurable with the XAI_BASE_URL environment variable.

  • speakerVoiceId (string), Defaults to eve. When authentication is provided, openclaw infer tts voices --provider xai retrieves the current built-in catalog; without it, the system lists offline fallbacks: ara, eve, leo, rex, and sal. Custom voice IDs from the account are forwarded even if they do not appear in the built-in list. Its legacy name is voiceId.

  • language (string), A BCP-47 language code, or auto. The default is en.

  • responseFormat (mp3" | "wav" | "pcm" | "mulaw" | "alaw), Defaults to mp3.

  • speed (number), A provider-specific speed adjustment, set to 0.7..1.5.

Xiaomi MiMo

  • apiKey (string), Configured via the XIAOMI_API_KEY environment variable.

  • baseUrl (string), Defaults to https://api.xiaomimimo.com/v1. Set using the XIAOMI_BASE_URL environment variable.

  • model (string), Defaults to mimo-v2.5-tts. Configurable with the XIAOMI_TTS_MODEL environment variable. Also accepts mimo-v2.5-tts-voicedesign.

  • speakerVoice (string), For preset-voice models, the default is mimo_default. Set via the XIAOMI_TTS_VOICE environment variable. Its legacy alias is voice. This value is not sent for mimo-v2.5-tts-voicedesign.

  • format (mp3" | "wav), Defaults to mp3. Configurable with the XIAOMI_TTS_FORMAT environment variable.

  • style (string), An optional natural-language style instruction sent as the user message; it is not spoken. For mimo-v2.5-tts-voicedesign, this serves as the voice-design prompt, and OpenClaw provides a default if one is not given.

Agent tool

The tts tool converts text into speech and returns an audio attachment for the reply. On Feishu, Matrix, Telegram, and WhatsApp, the audio is sent as a voice message rather than a file. When ffmpeg is available, Feishu and WhatsApp can transcode non-Opus TTS output along this path.

On WhatsApp, audio is sent through Baileys as a PTT voice note (audio with ptt: true), and visible text is delivered separately from the PTT audio because clients do not reliably render captions on voice notes.

The tool can take optional channel and timeoutMs fields; timeoutMs sets a per-call provider request timeout in milliseconds. Values provided per call override tts.timeoutMs; any TTS timeout configured here takes precedence over a plugin defined provider default.

Gateway RPC

MethodPurpose
tts.statusRetrieve the current TTS state and details of the last attempt.
tts.enableChange the local auto preference to always.
tts.disableSwitch the local auto preference to off.
tts.convertConvert text to audio in a single request.
tts.setProviderUpdate the local provider preference.
tts.personasShow all configured personas and identify which one is active.
tts.setPersonaAdjust the local persona preference.
tts.providersDisplay all configured providers along with their status.