Synthesize Speech
Use this action when you have text and want a spoken audio file of it, saved in your company storage.
Best for
- Speaking an order confirmation, an appointment reminder or an IVR prompt
- Producing a voice note to send back over a channel
- Turning text an earlier step produced into an audio file you can attach or store
Use something else when
| You want | Use |
|---|---|
| A transcript of speech, not speech from text | Transcribe audio — it goes the other direction |
| To re-encode audio you already have | Transform media — this action only makes new audio from text |
| To speak text that came from an LLM or a rich-text field | Prepare spoken text first, then this action |
Main fields
| Field | What it does |
|---|---|
| Text | The text to speak, at most 4096 characters. Usually step(N).text from an earlier step |
| Voice profile | Which voice to use. Leave it empty to use your company default |
| Format | mp3 (the default) or opus |
| Purpose | Optional label for what this step is for, shown in the logs |
Only Text takes an expression. Voice profile, Format and Purpose are read exactly as you set them, so a CEL expression typed into one of those is not evaluated — it is discarded, and you silently get the default voice or format instead. Pick them from their fields.
The audio is ready when the step finishes
Unlike Transcribe audio and Transform media, this action does not queue a job. It synthesizes the audio inline, so there is nothing to poll and nothing to wait for.
By the time the next step runs, step(N).file names an audio object that is really there. You can attach it, store it, or pass it straight to another media step in the same run.
What later steps can use
| Value | What it is |
|---|---|
step(N).file | The {bucket, fullPath} reference every media action produces. Feed it to Send message to contact attachments, a file field, or Inspect media |
step(N).bucket | The storage bucket the audio was written to |
step(N).fullPath | The full path of the audio object |
step(N).size | The size of the audio in bytes |
step(N).contentType | audio/mpeg for mp3, audio/ogg for opus |
step(N).format | The format that was actually used |
step(N).cache | hit when a recent identical request was reused, otherwise miss |
step(N).billedMeter | tts_ms when the synthesis was billed, audio_ms_byok when it ran on your own provider key and was not billed, or empty on a cache hit |
step(N).billedMs | The milliseconds recorded on billedMeter, and 0 on a cache hit |
Read the outcome with step(N).billedMs, not step(N).status — status is reserved for HTTP status codes, and this action deliberately never writes it. Use step_ok(N) to test whether the step succeeded.
Voices
A voice profile holds the voice, provider and settings used to speak your text. You manage profiles under your company settings, and one of them can be marked as the company default.
This action picks a voice in this order:
- The Voice profile you named on the step
- Your company's default profile
- The built-in platform voice, so synthesis works before you configure anything
A voice pinned to a channel is not used here. That pin only applies inside the app, where a conversation identifies its channel. A workflow has no conversation to resolve, so if you want a specific voice, name the profile on the step.
Limits
| Limit | Value |
|---|---|
| Text length | 4096 characters, roughly 700 words or five minutes of speech |
| Speech requests | 60 per minute, per company |
Longer text is rejected, not split. There is no server-side chunking and no joining of audio, so text over the limit fails rather than producing a partial file. If you need more, split the text across several steps and accept one audio file per step.
The 60-per-minute ceiling is shared with voice mode in the app and with Prepare spoken text, so a prepare-then-speak pair costs two of those requests per run. A tight synthesis loop can use up the allowance and interrupt someone using voice in the app at the same time — pace the workflow if it synthesizes in bulk.
Cost
Synthesis counts toward your plan's audio usage, based on the length of the text rather than the length of the finished audio. Voices differ a lot in price: some cost several times more per minute than others, so the voice profile you pick changes the bill.
Profiles that use your own provider key are tracked but not billed by AutoTalk — your provider bills you directly. Check step(N).billedMeter before reading step(N).billedMs: on your own key that number is an estimate for your own records, not a charge.
Speaking the same text again shortly afterwards is usually free. A repeat of identical text with the same voice and format within about ten minutes reuses the earlier audio, makes no provider call, and reports cache: hit with billedMs: 0. This is a best-effort reuse, not a guarantee — a busy period or a restart can turn it back into a normal, billed call. Audio produced on your own provider key is never reused this way.
A cache hit still writes a new file. Every call stores its own audio object, so a workflow that speaks the same text in a loop keeps growing your storage usage even while the synthesis itself costs nothing.
When it fails
| Code | What happened |
|---|---|
speech_missing_text | The Text expression resolved to nothing |
speech_text_too_long | The text is over 4096 characters. Split it across steps |
speech_format_invalid | Format is something other than mp3 or opus |
voice_profile_not_found | The profile named on the step does not exist |
voice_profile_id_invalid | Voice profile does not hold a valid profile reference — often an expression that was not evaluated |
limits_reached | Your plan's audio allowance for the month is used up |
rate_limit_error | More than 60 speech requests in the same minute. Worth retrying shortly |
storage_quota_exceeded | The audio was produced but your storage is full, so it could not be saved |
speech_synthesis_failed | The voice provider could not be reached or refused the request. Worth retrying |
storage_quota_exceeded is the one to watch: the audio was synthesized and counted before the save was attempted, so the usage is spent even though no file arrived. Free up storage before retrying.
Elsewhere
The same capability is available outside workflows as the synthesize_speech MCP tool and as POST /v1/speech on the API. All three produce the same audio and the same fields; only where the file is stored differs.