Docs / FreeSWITCH integration

Integrate AMDY AMD with FreeSWITCH

AMDY.IO AI answering machine detection for FreeSWITCH uses mod_audio_fork to stream answered-call audio to the AMDY WebSocket endpoint at ws://api.amdy.io:2700 and route each call on the classification result. The same backend powers the ViciDial and Asterisk integrations, so results, timing, and your dashboard stay consistent.

Running ViciDial or plain Asterisk instead? Use the ViciDial guide or the Asterisk install guide. This page covers the FreeSWITCH integration only.

What does this integration do?

The integration attaches an audio fork to every answered call, streams mono 8 kHz 16-bit PCM to the AMDY detection service, and waits for a classification. The result is exposed as channel variables that your dialplan branches on. Only HUMAN results reach an agent; everything else is dispositioned automatically.

mod_audio_fork cannot send custom HTTP headers on the WebSocket upgrade, so fork connections carry no X-API-Key. AMDY authorizes fork connections by IP allowlist instead. The installer registers this host's public IP against your account using your API key.

What do you need before you start?

  • FreeSWITCH with mod_audio_fork available (see note below)
  • Root access on the FreeSWITCH server
  • Your AMDY.IO API key, from Settings
  • Outbound TCP to api.amdy.io:2700

There is no verified prebuilt download URL for mod_audio_fork. The upstream drachtio repository was deleted. The installer accepts a prebuilt .so via --module (recommended), or builds from source when a configured FreeSWITCH source tree is available via --fs-src. If neither path works, the installer fails with instructions rather than downloading from an unverified source.

How do you install the AMDY FreeSWITCH integration?

Run the installer as root with your API key:

bash installamd-freeswitch.sh <API_KEY> [options]

Or pass a prebuilt module and a key together:

bash installamd-freeswitch.sh --api-key <KEY> --module /path/to/mod_audio_fork.so

Key installer options:

FlagPurpose
--module PATHInstall this prebuilt mod_audio_fork.so (recommended)
--fs-src PATHFreeSWITCH source tree (configured) to build mod_audio_fork from source
--source-repo URLGit repo with mod_audio_fork source (default: mdslaney/drachtio-freeswitch-modules clone)
--dialplan PATHPath to amdy-fork.xml (default: embedded copy in the installer)
--skip-registerSkip IP registration with the portal
--skip-buildDo not attempt the build-from-source path

The installer:

  1. Installs and loads mod_audio_fork in modules.conf.xml
  2. Drops the amdy-fork.xml dialplan snippet and the amdy_fork.lua driver script
  3. Stores the API key in /etc/amdy/api-key (mode 600)
  4. Registers this server's public IP with your AMDY account via POST /api/v1/ips/register

How do you verify the install?

The health check script confirms six conditions in one run: FreeSWITCH is running, the event socket responds, mod_audio_fork is loaded, the amdy dialplan context is parsed, api.amdy.io:2700 is reachable, and the API key file exists.

bash check-amdy-freeswitch.sh

Or confirm the module manually. You should see true:

fs_cli -x "module_exists mod_audio_fork"

The check script exits 0 when all checks pass and 1 otherwise. It is safe to run repeatedly; it only reads state and never modifies the dialplan or module files.

What does the dialplan do on answer?

  1. Answers the call. Media must be flowing before the fork attaches.
  2. Runs amdy_fork.lua, which starts the audio fork:
uuid_audio_fork <uuid> start ws://api.amdy.io:2700 mono 8k {"config":{"sample_rate":8000,"VID":"<caller_id_name or Unknown>"}}
  1. The config frame carries the call identity. VID maps to caller_id_name in FreeSWITCH (equivalent to agi_calleridname in Asterisk). If empty, AMDY records it as Unknown.
  2. The Lua driver waits up to amdy_max_wait_ms (default 10000, clamped 1000–30000) for a classification from the server. It listens for mod_audio_fork::transcription and mod_audio_fork::json events filtered by the channel UUID.
  3. When the deadline passes or a result arrives, the driver stops the fork by sending {"eof":1} as the final text frame, then sets channel variables for the dialplan to branch on.
  4. The dialplan transfers to amdy_dispatch_${amdy_result}, which routes each result to its own extension.

What result values does AMDY return and how are they routed?

amdy_resultActionHangup cause
HUMANBridge to amdy_bridge_targetNORMAL_CLEARING (if bridge fails)
MACHINETransfer to amdy_voicemail_extension in amdy_voicemail_context
FASHangupCALL_REJECTED
TIMEOUTHangupRECOVERY_ON_TIMER_EXPIRE
ERRORHangupNETWORK_OUT_OF_ORDER

Before routing, set amdy_bridge_target, amdy_voicemail_extension, and amdy_voicemail_context on the channel. If HUMAN fires with no bridge target set, the call hangs up with INCOMPATIBLE_DESTINATION.

What channel variables does the Lua driver set?

VariableValuesMeaning
amdy_resultHUMAN | MACHINE | FAS | TIMEOUT | ERRORThe classification the dialplan branches on
amdy_causeHUMAN | MACHINE | FAS | AUDIO_TIMEOUT | FORK_START_FAILED | SERVER_ERROR | CONNECTION_REFUSED | CONNECTION_LOSTShort marker or error label
amdy_statsRaw server response bodyFull response text from AMDY, if one arrived

What are the common problems and their fixes?

SymptomLikely cause
No result, call ends after ~10sOutbound TCP to api.amdy.io:2700 blocked. Check firewall rules.
Connection rejected right after openingThis host's IP is not allowlisted. Re-run the installer registration.
TIMEOUT on every callFork not running, or no read-direction audio at 8 kHz. Increase amdy_max_wait_ms if the greeting is long.
ERROR with FORK_START_FAILEDmod_audio_fork not loaded. Run: fs_cli -x "load mod_audio_fork"
Machines reaching agentsRouting matches too broadly. Bridge only on HUMAN.
Every call logs VID as UnknownCaller ID name not set before the fork starts.
module_exists returns falseModule not loaded. Re-run the installer, check modules.conf.xml.

Frequently asked questions

Does the integration support TLS (wss://)?

All verified source uses plain ws:// on port 2700. No wss:// endpoint is documented in the installer or client code. Contact us for integration access if your deployment requires TLS.

Why is there no X-API-Key on the WebSocket?

mod_audio_fork does not expose custom HTTP headers on the WebSocket upgrade request. AMDY authorizes fork connections by IP allowlist instead. The installer registers this host's public IP against your account using your API key over the portal REST API.

What audio format does AMDY expect?

8 kHz, 16-bit, mono, raw PCM (signed little-endian). The fork command uses mono 8k to match.

How long does AMDY wait for a result?

The Lua driver defaults to 10000 ms, clamped between 1000 ms and 30000 ms. Override it per call by setting amdy_max_wait_ms on the channel before running the Lua script.

Support

Need help? Email [email protected] with your FreeSWITCH version (fs_cli -x "version") and the output of the check script.

Not an AMDY customer yet? Sign up at app.amdy.io to get your API key and start detecting.