nomoVM
FreeSWITCH integration

FreeSWITCH answering machine detection with AI

nomoVM adds FreeSWITCH answering machine detection that listens to how a call is answered. A Lua script streams the first seconds of the answered call through mod_audio_stream, and your dialplan gets AMDSTATUS: HUMAN, MACHINE or FALLBACK, in about 2 seconds.

Why FreeSWITCH needs better AMD

FreeSWITCH has no built-in answering machine detection like Asterisk's AMD(). Options are a beep detector (mod_avmd, which finds the voicemail tone after the greeting, not who answered), third-party timing-based modules, or your carrier's detection. nomoVM gives FreeSWITCH dialers and PBXs the same AI model VICIdial users get: it hears the difference between a live "Hello?" and a recording, recognises busy, SIT, fax and ringback tones, and catches recorded hellos played to many numbers. How the AI decides

How it works

  1. The call is answered

    Your dialplan runs nomovm_amd.lua. The script starts mod_audio_stream on the call, which sends the caller's audio (8 kHz, mono) to nomoVM over a secure WebSocket, with your key in the request header.

  2. nomoVM decides

    After about 2 seconds of audio (3.2 seconds for unclear calls) the service replies with one verdict. mod_audio_stream passes it back to FreeSWITCH as an event.

  3. Your dialplan routes

    The script sets AMDSTATUS and AMDCAUSE, stops the stream and returns. Send HUMAN to an agent, drop or leave a message for MACHINE.

If nomoVM can't judge the call (wrong key, plan limit, service unreachable, or mod_audio_stream not loaded), AMDSTATUS is FALLBACK before any audio is used. A timeout after streaming started gives MACHINE / UNKNOWN, so only a confirmed person reaches an agent.

Install

1. Load mod_audio_stream

mod_audio_stream is an open-source FreeSWITCH module that streams call audio to a WebSocket. Install it as its README describes (it publishes packages for recent Debian releases with FreeSWITCH from SignalWire's repository), then load it:

fs_cli -x "load mod_audio_stream"
# and to load it at every start, in autoload_configs/modules.conf.xml:
#   <load module="mod_audio_stream"/>

mod_audio_stream is a separate project with its own license (AGPL-3.0 with a FreeSWITCH linking exception for the community edition). Check that it suits how you run FreeSWITCH.

2. Install the script

cd /usr/share/freeswitch/scripts      # or /etc/freeswitch/scripts on some installs
curl -fsSLO https://nomovm.com/assets/nomovm_amd.lua

mod_lua ships with FreeSWITCH. The script has no other dependencies.

3. Create a key

Sign up free and create a WS key on the WS keys page (one per FreeSWITCH server is easiest to follow in the dashboard).

Dialplan

Send answered outbound calls to an extension that runs the script, then route on the result. In dialplan/default.xml (or your own context):

<extension name="nomovm_amd">
  <condition field="destination_number" expression="^nomovm_amd$">
    <action application="lua" data="nomovm_amd.lua wss://amd.nomovm.com/ YOUR_KEY 4500"/>
    <action application="transfer" data="amd_${AMDSTATUS} XML default"/>
  </condition>
</extension>

<extension name="amd_human">
  <condition field="destination_number" expression="^amd_HUMAN$">
    <action application="transfer" data="agents XML default"/>   <!-- your agent queue -->
  </condition>
</extension>

<extension name="amd_machine">
  <condition field="destination_number" expression="^amd_MACHINE$">
    <action application="hangup"/>   <!-- or play a voicemail message first -->
  </condition>
</extension>

<extension name="amd_fallback">
  <condition field="destination_number" expression="^amd_FALLBACK$">
    <action application="transfer" data="agents XML default"/>   <!-- or your previous detection -->
  </condition>
</extension>

Variables in action data are filled in when the action runs, so amd_${AMDSTATUS} becomes amd_HUMAN, amd_MACHINE or amd_FALLBACK after the script has decided. AMDCAUSE tells you why (SCREENING, TONE, BUSY, SILENCE...) if you want separate routes.

Outbound calls

Have your dialer place each call into that extension, so detection starts the moment the call is answered:

originate {origination_caller_id_number=15550001111}sofia/gateway/my_carrier/15551234567 nomovm_amd XML default

Dialers that use the event socket can do the same with bgapi originate. The extension runs only after the callee answers, which is when AMD should start.

Test

  1. Check the module answers: fs_cli -x "uuid_audio_stream" should print its usage, not "command not found".
  2. Place a test call to your own mobile and let it go to voicemail: the log line shows MACHINE. Answer it yourself: HUMAN.
  3. Open the nomoVM dashboard: each call is listed with its verdict, cause, timing and the audio the model heard.

FreeSWITCH questions

Does it work with FusionPBX and other FreeSWITCH-based systems?

Yes, if you can load mod_audio_stream and add a dialplan extension. FusionPBX lets you add dialplan entries from its admin screens.

What audio does nomoVM receive?

Only the caller's side ("mono"), 8 kHz, 16-bit, from answer until the verdict, usually 2 to 3.2 seconds. The script stops the stream as soon as the verdict arrives.

How is the key sent?

As an Authorization header on the WebSocket handshake (mod_audio_stream's STREAM_EXTRA_HEADERS), over TLS. It never appears in the URL.

Can I use mod_avmd as well?

Yes. mod_avmd detects the voicemail beep, which is useful for timing a voicemail drop after nomoVM has said MACHINE.

Not using mod_audio_stream?

Any module or application that can stream 8 kHz, 16-bit audio to a WebSocket can use the WebSocket API directly.

Stop sending voicemail to your agents

30,000 calls a month free. No card, no setup fee. Your dialer falls back to Asterisk AMD() on its own if anything goes wrong.