Voice Response
Text Response, out loud. Three fields matter, and the one that decides whether you get audio at all defaults to a value that gives you none.
Some answers have to be heard. A confirmation read out on a call, a prompt in a phone menu, a message left on someone's voicemail — none of those are text, and none of them can wait for the person to open an app.
Voice Response is the node that speaks. It takes text — usually text some earlier node produced — hands it to a speech provider, and gives you back audio. In shape it is Text Response with a voice attached: same templated field, same place at the end of a chain.
In practice it has one decision that the rest of the node hangs on, and the default for that decision is wrong for every real workflow. Start there.
The fields
| Field | What it holds |
|---|---|
text | Text to convert to speech (supports template variables) Defaults to {{output}}. |
synthesizer_source | Source for synthesis service One of: direct · voice_agent · none Defaults to none. |
synthesizer_integration_id | Text-to-speech integration to use (when source is 'direct') |
output_format | Audio output format One of: mp3 · wav · ogg · m4a Defaults to mp3. |
output_destination | Where to send the audio output One of: file · stream · phone · webrtc Defaults to file. |
quality | Audio quality setting One of: low · medium · high Defaults to medium. |
text
- What it holds
- Text to convert to speech (supports template variables) Defaults to
{{output}}.
synthesizer_source
- What it holds
- Source for synthesis service One of:
direct · voice_agent · noneDefaults tonone.
synthesizer_integration_id
- What it holds
- Text-to-speech integration to use (when source is 'direct')
output_format
- What it holds
- Audio output format One of:
mp3 · wav · ogg · m4aDefaults tomp3.
output_destination
- What it holds
- Where to send the audio output One of:
file · stream · phone · webrtcDefaults tofile.
quality
- What it holds
- Audio quality setting One of:
low · medium · highDefaults tomedium.
The default synthesizer_source is none, and none means no speech is produced. It is not “use the default provider” — it is “do not start one”. The node then reports a successful run with no audio attached. If you take one thing from this lesson, take this: set synthesizer_source to direct and name an integration.
The three sources, plainly:
direct— you name a synthesizer integration insynthesizer_integration_id. This is the one you want. Which voice, from which provider, decided here.voice_agent— borrow the settings from a Voice Agent node in the same workflow, so the spoken reply matches the agent's voice. Only sensible when there is such a node.none— no synthesizer. The node still runs and still reports success.
A worked example
A confirmation, spoken. The text is built from what the workflow found, and the synthesizer is named rather than assumed.
Text in, an audio file out
synthesizer_source is direct, and the integration is named. That is the difference between audio and silence.
Scroll for all 3 steps →
The text field templates exactly like Text Response, with the same rule about only seeing what is wired directly into it. Everything else on that node is about the audio rather than the words: the format, where it goes, how hard the provider should work.
Write for the ear. This is the part no configuration helps with. A sentence that reads well can be unlistenable — a URL, a reference number, a bulleted list. Say “we'll email you the link” rather than reading the link. Break long numbers into groups. Read your text aloud before you save it; if you stumble, so will the synthesiser.
Where the audio goes
| Destination | What happens |
|---|---|
file | An audio file you can fetch from the node's audio_url. The one that works today, and the one to build on. |
phone · webrtc · stream | Named in the panel, and not implemented. Selecting one logs a line and does nothing — the audio is still synthesised, it just is not delivered anywhere. |
file
- What happens
- An audio file you can fetch from the node's audio_url. The one that works today, and the one to build on.
phone · webrtc · stream
- What happens
- Named in the panel, and not implemented. Selecting one logs a line and does nothing — the audio is still synthesised, it just is not delivered anywhere.
That is worth being blunt about because the dropdown reads like four working options. If you need audio on a live call, the node that owns the call places it there — see the next three lessons. Voice Response's job is to produce the audio.
What comes back
| Reference | What you get |
|---|---|
{{alias.audio_url}} | Where the audio is. The field everything downstream actually needs — and the field to check when you suspect nothing was produced. |
{{alias.response_text}} | The text that was spoken, after templating. Useful for logging what the caller heard. |
{{alias.synthesis_metadata}} | Duration, character count, voice settings, provider. Also where the error goes when synthesis fails. |
{{alias.audio_url}}
- What you get
- Where the audio is. The field everything downstream actually needs — and the field to check when you suspect nothing was produced.
{{alias.response_text}}
- What you get
- The text that was spoken, after templating. Useful for logging what the caller heard.
{{alias.synthesis_metadata}}
- What you get
- Duration, character count, voice settings, provider. Also where the error goes when synthesis fails.
What breaks
A failed synthesis is reported as a successful node. If the synthesiser cannot start — no source configured, or the integration is wrong — the failure is caught inside the node, recorded in synthesis_metadata and then discarded. The node returns success and leaves down its normal path with audio_url set to nothing. Nothing in the execution log looks wrong. The test is always the same: read audio_url, not the node's status.
Empty text is the one thing it does treat as an error. If text resolves to nothing — usually because the default {{output}} found nothing upstream — the node fails properly and leaves down its error path. So the two failure modes look opposite: no words is loud, no voice is silent.
Long text costs real money and real seconds. Synthesis is billed by the character and takes time proportional to length. A node that speaks a whole AI answer is a node that occasionally speaks four hundred words. Cap it upstream — ask the model for one or two sentences — rather than discovering the bill.
Try it
- Add a Voice Response with a fixed sentence in
textand leave everything else alone. Run it and read{{alias.audio_url}}— it will be empty, and the run will be green. That is the default. - Set
synthesizer_sourcetodirect, pick an integration, and run it again. Fetch the URL and play it. - Change
voice_settingsspeed to 0.8 and again to 1.3, and listen to both. Pick the one you would want on a phone call — it is usually slower than you expect. - Clear
textentirely and run it. This time the node fails and takes the error path, which is the opposite of the first step and worth feeling the difference.
Next: Voice Inbound Call — what happens when the phone rings and the workflow, rather than a person, decides what to do about it.
Related lessons
Base rates — what a piece of evidence is actually worth
A face-recognition system that is 99.9% accurate and almost entirely wrong, and a number that sent an innocent woman to prison. Both are the same arithmetic, and it is the arithmetic that decides what any piece of evidence is worth.
ReadConfirmation and survivorship — what you never looked for
Two questions about evidence you did not go looking for. One is a rule you have to discover, and one is a pattern in five famous people — and in both, the thing that would have told you the truth is the thing nobody checks.
ReadLoss aversion, sunk cost and regression — what it costs you
Four questions you answer about yourself rather than about a scenario, and your own answers are the finding. Then the pattern that makes praise look useless and criticism look like it works, whatever you actually do.
Read
