Voicemail & Answering Machine Detection
Take a message when nobody answers (record@v1 + saveRecordingFile@v1), notify your team by email or SMS, and detect answering machines on outbound calls with amd@v3.
Two sides of the same problem: inbound, nobody picks up and the caller wants to leave a message; outbound, your campaign reaches a voicemail and you want to know, leave a message, or hang up.
📥 Inbound: Take a Message
How It Works
dial@v2tries to reach your team.$dial.okisfalsewhen nobody answered.record@v1plays a beep and records the caller until silence, a key press, ormaxLength. Its result is a temporary file.saveRecordingFile@v1turns that file into a Recording of your account (an MP3 URL, listed in the Recordings API), with an optional transcript.events.readyofsaveRecordingFile@v1runs when the recording (and transcript) is ready: email it, text it, push it to your CRM.
Example: Forward, Then Voicemail
description: Forward the call, take a message if nobody answers, email it
compat:
version: "2025-09-01"
defaults:
language: en-US
variables:
$team: "+33199001020"
$notify: [email protected]
$dial: null
$record: null
$recording: null
branches:
inbound-call:
actions:
- action: dial@v2
params:
targets:
- number: ${{ $team }}
timeout: 25
result: $dial
# Nobody answered: take a message
- if: '!$dial.ok'
then:
- action: play@v3
params:
tts:
text: We are not available right now. Please leave a message after the beep, and press any key when you are done.
- action: record@v1
params:
maxLength: 120
timeout: 5 # stop after 5 seconds of silence
result: $record
- action: saveRecordingFile@v1
params:
file: ${{ $record }}
transcription:
enabled: true
language: en-US
events:
ready:
- action: email@v1
params:
to: ${{ $notify }}
subject: New voicemail from ${{ call.fromNumber || "an anonymous caller" }}
text: |
Caller: ${{ call.fromNumber || "anonymous" }}
Duration: ${{ recording.duration }} seconds
Listen: ${{ recording.url }}
Transcript:
${{ chat_text }}
result: $recording
- action: play@v3
params:
tts:
text: Thank you, goodbye.
- action: hangup@v1
events.readyruns after the call, like thehangupbranch. Usesms@v1instead ofemail@v1to text the team, orfetch@v3to create a ticket. See Record and Transcribe Calls for the variables available there.
Let the Caller Review the Message
playRecordingFile@v1 plays the temporary file back before you save it.
- action: record@v1
params:
maxLength: 120
result: $record
- action: play@v3
params:
tts:
text: Here is your message.
- action: playRecordingFile@v1
params:
file: ${{ $record }}
- action: gather@v4
params:
tts:
text: Press 1 to send it, or 2 to record it again.
maxDigits: 1
timeout: 10
result: $choice
- if: $choice.input === "1"
action: saveRecordingFile@v1
params:
file: ${{ $record }}
result: $recordingrecord@v1 Parameters
record@v1 Parameters| Parameter | Default | Description |
|---|---|---|
maxLength | 30 | Maximum length of the message, in seconds (5 to 300). |
timeout | 5 | Seconds of silence that end the recording (0 to 10). |
beep | true | Play a beep before recording. |
finishOnAnyKey | true | Any key press ends the recording. |
📤 Outbound: Detect Answering Machines
How It Works
On a Broadcast Call, the called party answers, and your scenario continues in the answerOnBranch branch — whether a human or a voicemail picked up. amd@v3 listens for a few seconds and tells you which one it is:
- a human says a short greeting ("Hello?") and waits;
- a machine plays a long greeting ("Hi, you have reached…, please leave a message after the tone").
amd@v3 counts the words it hears: more than minWords means a machine. yesHints and noHints short-circuit the decision when a known word is heard.
Example: Leave a Message on Voicemails Only
description: Call a list of people; talk to humans, leave a message on voicemails
compat:
version: "2025-09-01"
defaults:
language: en-US
variables:
$target: "+33639980010" # passed when starting the scenario
$dial: null
$amd: null
$silence: null
branches:
api-initiated:
actions:
- action: dial@v2
params:
callerId: "+33199001234"
targets:
- number: ${{ $target }}
timeout: 30
answerOnBranch: answered
result: $dial
answered:
actions:
- action: amd@v3
params:
timeout: 5
minWords: 4
yesHints: [message, voicemail, tone, beep]
noHints: [hello, yes]
result: $amd
# A voicemail: wait for the end of the greeting, then leave the message
- if: $amd.detected === "machine"
then:
- action: waitForSilence@v1
params:
silence: 2
timeout: 20
result: $silence
- action: play@v3
params:
tts:
text: Hello, this is Acme. Your order is ready for pickup. See you soon!
- action: hangup@v1
# A human (or no decision): talk to them
- action: play@v3
params:
tts:
text: Hello, this is Acme. Your order is ready for pickup.
- action: gather@v4
params:
tts:
text: Press 1 to confirm you will come today, or 2 for tomorrow.
maxDigits: 1
timeout: 10
result: $answer
- action: hangup@v1
Answering machine detection is a heuristic: it listens to the first seconds of the call. A human who answers with a long sentence can be mistaken for a machine, and a short voicemail greeting for a human. Keeptimeoutshort (5 seconds is a good start) and tuneyesHints/noHintswith the words your callees actually use.
amd@v3 Result and Parameters
amd@v3 Result and Parametersresult:
detected: machine # machine | human | "" (no decision before the timeout)
recognized: message # the hint that matched, if any
timeout: false| Parameter | Default | Description |
|---|---|---|
timeout | 6 | Seconds to listen (1 to 10). |
minWords | 4 | More words than this means a machine. |
yesHints | — | Words that mean a machine, in the callee's language (up to 10). |
noHints | — | Words that mean a human (up to 10). |
language | — | Speech-to-text language. Defaults to the language of the called number. |
tts / audioFile | — | Optional prompt played while listening (set at most one). Key presses are ignored. |
💡 Going Further
- Business hours: send callers straight to voicemail outside opening hours, with a
javascript@v1check oncall.startedAt. - Keep a log of every attempt: push
$dial.statusand$amd.detectedto your server from thehangupbranch, so your campaign can retry the voicemails later. See Push Data to Your Platform. - Summarize voicemails with AI: in
events.ready, sendchat_texttoopenai@v3before emailing it. See Record and Transcribe Calls.
Updated about 3 hours ago
Did this page help you?
