Integrations
FreeSWITCH
A Lua script that forks the callee's audio to VM Hunter through mod_audio_fork or mod_audio_stream, then sets AMDSTATUS and AMDCAUSE on the channel. A drop-in XML dialplan routes the call on the result.
Prerequisites
| Requirement | Notes |
|---|---|
| FreeSWITCH 1.10+ | With mod_lua loaded (it is in the default build). |
| An audio streaming module | Either mod_audio_fork (drachtio) or mod_audio_stream. Both send 8 kHz mono PCM over a WebSocket, which is exactly what VM Hunter expects. The script detects whichever one is loaded. |
| API key | From Dashboard → API Keys. |
| Network | Outbound TCP to app.vmhunter.com:2701. |
mod_audio_stream is the lighter build: it has no dependencies beyond libwebsockets. Follow its README, then add <load module="mod_audio_stream"/> to modules.conf.xml.Install
Download the client
Two files: the Lua script and an XML dialplan include.
Terminalwget -N -O vmhunter-freeswitch.tar.gz https://app.vmhunter.com/vmhunter-freeswitch.tar.gz tar zxvf vmhunter-freeswitch.tar.gz cp vmhunter_amd.lua /usr/share/freeswitch/scripts/ cp vmhunter_amd.xml /etc/freeswitch/dialplan/default/Paths vary by packaging: on source builds use
/usr/local/freeswitch/scriptsand/usr/local/freeswitch/conf/dialplan/default. The XML goes in whichever context your dialer originates into.Set your API key
The script reads channel variables first and global variables second, so you can set the key once in
vars.xmlor pass it per call in the originate string.conf/vars.xml<X-PRE-PROCESS cmd="set" data="vmhunter_api_key={YOUR_API_KEY}"/> <!-- optional --> <X-PRE-PROCESS cmd="set" data="vmhunter_ws_url=ws://app.vmhunter.com:2701"/>Reload
Terminalfs_cli -x "reloadxml"Originate calls into the AMD extension
Send the customer leg to
vmhunter_amdso detection starts the moment the callee answers.vidis the lead or call id that VM Hunter bills and records under; it falls back to the channel UUID.fs_clioriginate {vid=12345,ignore_early_media=true,origination_caller_id_number=18005551212}sofia/gateway/carrier/15551234567 vmhunter_amd XML defaultWatch the result in the console:
Terminalfs_cli /log 6 # [vmhunter] uuid=... vid=12345 module=audio_stream raw=MACHINE -> status=MACHINE cause=MACHINE_BEEP waited=2300ms
Routing the verdict
The bundled dialplan runs the script, then transfers to vmhunter_route, which sends the call on to vmhunter_HUMAN or vmhunter_MACHINE. Edit those two extensions for your setup; everything above them can stay as it is.
<extension name="vmhunter_HUMAN">
<condition field="destination_number" expression="^vmhunter_HUMAN$">
<!-- hand the live person to your agents: a callcenter queue, a fifo, or a bridge -->
<action application="callcenter" data="sales@default"/>
</condition>
</extension>
<extension name="vmhunter_MACHINE">
<condition field="destination_number" expression="^vmhunter_MACHINE$">
<!-- disconnected numbers get their own disposition so the dialer does not recycle the lead -->
<condition field="${AMDCAUSE}" expression="^DISCONNECT">
<action application="set" data="dialer_disposition=DISCONNECT"/>
<anti-action application="set" data="dialer_disposition=MACHINE"/>
</condition>
<action application="hangup" data="NORMAL_CLEARING"/>
</condition>
</extension>Call screening (CALLGUARD)
Screened calls (iPhone “Ask reason for calling”, Google Call Screen) are labelled AMDCAUSE=CALLGUARD_PHRASE:<phrase>. The bundled vmhunter_route extension plays a short intro so the screener hears who is calling, marks the call HUMAN and connects an agent:
<extension name="vmhunter_route">
<condition field="destination_number" expression="^vmhunter_route$"/>
<condition field="${AMDCAUSE}" expression="^CALLGUARD">
<action application="playback" data="$${sounds_dir}/vmhunter/callguard-intro.wav"/>
<action application="set" data="AMDSTATUS=HUMAN"/>
<action application="transfer" data="vmhunter_HUMAN XML default"/>
<anti-action application="transfer" data="vmhunter_${AMDSTATUS} XML default"/>
</condition>
</extension>Record the intro as 8 kHz mono WAV at sounds/vmhunter/callguard-intro.wav. To keep dropping screened calls instead, delete the vmhunter_route extension and transfer straight to vmhunter_${AMDSTATUS}.
Configuration
Variables the script reads (channel variable first, then global):
| Variable | Default | Purpose |
|---|---|---|
vmhunter_api_key | — | Your API key. Required. |
vmhunter_ws_url | ws://app.vmhunter.com:2701 | Engine endpoint. |
vid | channel UUID | Lead or call id shown in your call logs. |
vmhunter_module | auto | audio_fork or audio_stream. Only needed if both modules are loaded. |
Two constants at the top of the script control the fail-safes:
| Constant | Default | Purpose |
|---|---|---|
CALLGUARD_AS | HUMAN | What AMDSTATUS becomes when the raw verdict is CALLGUARD. |
FAILED_AS | MACHINE | What AMDSTATUS becomes when the engine errors or never replies. |
WAIT_MS | 6000 | How long to wait for a verdict before applying FAILED_AS. |
Channel variables set on every call:
| Variable | Values |
|---|---|
AMDSTATUS | HUMAN or MACHINE. Route on this one. |
AMDSTATUS_RAW | The engine's untouched verdict: HUMAN, MACHINE, CALLGUARD or FAILED. |
AMDCAUSE | The reason, e.g. MACHINE_BEEP, INITIALSILENCE, CALLGUARD_PHRASE:…. Full list in the API reference. |
Troubleshooting
| Symptom | Check |
|---|---|
AMDCAUSE=NOT_RUN | vmhunter_api_key is empty. Set it in vars.xml or the originate string. |
AMDCAUSE=STREAM_START_FAILED | The streaming module is not loaded, or its uuid_audio_fork / uuid_audio_stream command failed. Run fs_cli -x "module_exists mod_audio_stream". |
AMDCAUSE=WS_CONNECT_FAILED | The module could not reach the engine. Test with nc -zv app.vmhunter.com 2701. |
AMDCAUSE=NO_REPLY | No verdict within WAIT_MS. Check for packet loss on the WebSocket and that the callee leg is actually answered. |
AMDCAUSE=AUTH_FAILED | The key is wrong or was regenerated. |