Detect machines
Machine detection lets SignalWire identify whether a person, voicemail, or a fax machine is on the call so your app can choose what happens next. This guide shows two flows: greet a person or leave a voicemail, and send a document when a fax machine answers.
How detection works
SignalWire listens to audio on an answered call. A short greeting followed by silence suggests a person; longer speech suggests voicemail or an IVR menu. Fax detection listens for a fax tone. If there isn’t enough audio to decide, your app needs a fallback for the uncertain result.
Voicemail detection can also wait for the greeting to end before you leave a message. The sections below explain how to handle these results in each approach.
Choose an approach
Follow the section for your app’s approach:
- SWML: add
detect_machineto your call document and branch on the result. Optionally send events to a webhook. - WebSocket (Relay): start detection on an answered call and handle events in your code. No public URL is needed.
- REST: start detection by call ID from any process and receive results at a webhook.
Prepare for detection
Start with working call setup from Make and receive calls. Test AMD (answering machine detection) with a phone you can answer or send to voicemail. To test fax detection, use a fax destination and a publicly accessible PDF.
A trial project only places calls to and receives calls from numbers you’ve purchased or verified. Prepare for your first call shows how to verify the phone you’ll test with.
Prerecorded and synthesized voicemail messages fall under consent, do-not-call, and calling-hour rules. Read the TCPA guide before you dial anyone but yourself.
Replace these values in the samples you run:
Detect machines via SWML
detect_machine pauses the document by default, then sets detect_result.
Use switch to choose the next action.
detect_machine_beep is true when the machine detector heard a beep; otherwise it is false.
With detectors: amd,fax, a fax tone can register as a beep, so a fax result can also report
true. detect_ms holds the elapsed milliseconds, or -1 with wait: false.
Detect voicemail via SWML
Use AMD to distinguish a person from voicemail, then branch on the result.
detect_message_end: true waits for a beep or a pause of machine_ready_timeout before the
machine branch plays. The default branch also greets unknown and failed results.
The SDK programs place the call as in Place a call via SWML. Each branch is built with the SDK’s SWML builder. The YAML and JSON tabs contain the equivalent complete document.
Run the example and answer with “Hello?” to hear the live confirmation. Run it again and send the call to voicemail to test the message after the greeting. The 45-second timeout allows time for the greeting; tune the silence threshold below if playback starts before the beep.
To reject voicemail instead, set detect_message_end: false and use hangup in the machine
branch. An AI handoff can use ai in the human branch.
Detect a fax machine via SWML
Detect whether a fax machine or a person answers with detectors: "amd,fax".
The fax branch sends a PDF, the human branch plays a spoken message, and the default
branch hangs up.
CED is the default tone, sent by an answering fax machine. A fax tone can resemble a voicemail
beep, so if amd decides first, SignalWire briefly waits for the fax detector. A detected fax
tone takes priority.
Test with a fax number to confirm document delivery, then with a phone to hear the spoken
fallback. For a fax machine calling you, use tone: "CNG" and
receive_fax instead; its status_url receives the document details.
Inbound call setup is covered in Answer a call via SWML.
Track detection results via SWML
Add status_url to detect_machine to receive JSON webhooks. These report intermediate detector
events; the document still branches on detect_result.
UNKNOWN doesn’t stop detection or resume the document. A silent call waits until timeout
before detect_result becomes unknown. If the call hangs up during machine detection, no
final finished callback is sent; fax detection still sends finished. Once set, beep: true
remains on later machine events.
For background detection, set wait: false and supply status_url. The document continues
immediately with detect_result: detecting; act on the webhook events instead. See the
webhooks guide for endpoint setup and testing.
Tune SWML detection
Set these properties on detect_machine. Change one at a time and test with the destinations
you expect to call.
For inbound screening, greet unknown callers and keep timeout short: they may be waiting
for you to speak. detect_interruptions is unsupported in SWML and makes detection fail.
The detect_machine reference lists all properties, including the
detectors selector, which defaults to both amd and fax.
Detect machines via WebSocket (Relay)
Start detection on an answered call with detect() and a machine or fax detector. Each
operation runs one detector, and a call can run only one detector of each type at a time.
Register a calling.call.detect handler before starting detection. Read event.detect.type
for the detector and event.detect.params.event for its result:
An action’s wait() returns on the first result. To leave voicemail, follow events through
MACHINE to READY instead. detect_message_end defaults to true; setting it to false
stops on MACHINE. A beep heard before MACHINE triggers MACHINE and then READY with
either setting; with false, detection has already stopped by the time a later beep plays, so
it’s usually missed.
UNKNOWN doesn’t stop the detector: it keeps listening until it decides or timeout expires.
finished follows the final result, including when the call hangs up during detection. Once a
beep is heard, event.detect.params.beep is true on that and later machine events; fax events
don’t carry it. Call the action’s stop() to stop detection early.
Detect voicemail via WebSocket (Relay)
Use AMD events to distinguish a person from voicemail and detect when the greeting ends.
The handler plays a live confirmation on HUMAN or UNKNOWN, leaves voicemail on READY,
and hangs up if detection finishes without a usable result. The announced flag prevents
later events from starting a second message.
Call setup follows Place a call via WebSocket (Relay).
Test once by answering with “Hello?” and once by sending the call to voicemail. To observe the
sequence, log event.detect in the handler. To reject machines, hang up on MACHINE instead;
to hand a person to an AI agent, call call.ai() on HUMAN.
Detect a fax machine via WebSocket (Relay)
Detect a fax machine by listening for CED, the default tone from an answering fax machine.
On detection, send the PDF; otherwise, play a spoken fallback. Unlike AMD, this detector
only decides whether it heard the fax tone; the fallback isn’t a human classification.
Listen for calling.call.fax with fax.type: finished to read fax.params.success and
fax.params.pages. These SDK versions don’t complete fax.wait() for that payload, so the
example handles the event directly.
Test with a fax destination, then with a phone to check the fallback. For incoming faxes,
listen for CNG and use receive_fax() in Python or receiveFax() in TypeScript. The finished
fax event also includes the stored document URL in fax.params.document. For inbound call
setup, see Answer a call via WebSocket (Relay).
Tune Relay detection
Pass timeout separately to detect(); put the other settings in the detector’s params.
Change one setting at a time. For inbound screening, greet UNKNOWN callers: they may be
waiting silently for you to speak. If you enable interruptions, handle NOT_READY and wait
for the next READY before resuming your message.
Detect on a call already in progress via REST
If another process needs to start detection, send calling.detect with the
answered call’s ID, a required control_id, a detect object, and status_url. Use
detect.type: machine for AMD or detect.type: fax for fax tones; pass settings in
detect.params and timeout separately. detect_message_end defaults to true, and fax
tone defaults to CED (use CNG for incoming faxes).
The command returns immediately. Your webhook receives calling.call.detect events with the
result in params.detect.params.event. Have the handler send the next call command, such as
calling.play on READY for voicemail. Use calling.detect.stop with the same control_id
to stop early. Only one detector of each type can run on a call at a time.