nomoVM
Docs

Answering machine detection API reference

The nomoVM answering machine detection API is one WebSocket connection per answered call: stream the call's audio, receive one JSON verdict. VICIdial, Asterisk and FreeSWITCH users don't need this page (see the VICIdial setup guide and the FreeSWITCH integration); it is for dialers that integrate directly.

Endpoint and authentication

wss://amd.nomovm.com/

White-label customers use their provider's WebSocket host instead. Send your API key on the WebSocket handshake:

Authorization: Bearer amd_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

X-API-Key: amd_live_... also works. Clients that can't set headers may put "api_key" in the start message instead. Create and revoke keys on the WS keys page of your dashboard. There is no IP allow-list to register: the key is enough.

Streaming

  1. Optional first message, text: {"type": "start", "call_id": "your-call-id"}. call_id (up to 64 characters) appears in your call logs.
  2. Binary messages: raw audio from the moment of answer. 8,000 Hz, 16-bit signed little-endian, mono (Asterisk "slin"). 100 ms frames (1,600 bytes) are recommended; any size works.
  3. Optional text {"type": "close"} if the call ends before a verdict: you get one at once, from the audio that arrived.

Keep streaming until the verdict arrives. After it, the server stops reading and closes the connection.

The verdict

One text message:

{"status": true, "amdstatus": "HUMAN", "amdcause": "HUMAN", "confidence": 0.97, "p_human": 0.97,
 "tone": "", "audio_ms": 2000, "window_ms": 2000, "extended": false, "elapsed_ms": 2013}
FieldMeaning
amdstatusHUMAN, MACHINE or FALLBACK. HUMAN only when amdcause is HUMAN.
amdcauseHUMAN, MACHINE, SCREENING, SILENCE, TONE, BUSY, UNKNOWN; for fallback see below.
confidence0 to 1, how sure the deciding check was.
p_humanThe model's probability that a person answered.
toneWhy a tone or repeat rule decided: busy, reorder, sit, fax, ringback, repeat:N (same audio as N earlier calls), repeat:known (a known carrier recording).
audio_ms, window_msAudio received and the window decided on (2000 or 3200).
extendedtrue when the call was unclear at 2 s and was decided at 3.2 s.
elapsed_msTime from connection to verdict on our side.

Fallback

When a call can't be judged, the answer comes immediately, before any audio is used, so you can run your own detection instead:

{"status": true, "amdstatus": "FALLBACK", "amdcause": "AUTH"}
amdcauseWhy
AUTHNo key, unknown key, or a key used on another brand's host.
SUSPENDEDThe key or account is disabled.
QUOTAThe free plan's monthly calls are used up, or a payment is overdue.
NOSERVERThe service can't judge calls right now. Your client also sets this when it can't connect.

Fallback answers are never billed. If anything fails after you started streaming (a timeout, a dropped connection), treat the call as MACHINE / UNKNOWN: only a confirmed human should reach an agent.

Timing

  • The verdict comes once 2.0 s of audio has arrived; for unclear calls, once 3.2 s has.
  • If audio stops for 0.6 s after at least 1 s arrived, the server decides with what it has.
  • Use a client timeout of 4,500 ms from connect. Shorter timeouts turn slow verdicts into dropped people.

Example (Python)

import asyncio, json, wave
from websockets.asyncio.client import connect

async def detect(path, key):
    async with connect("wss://amd.nomovm.com/",
                       additional_headers={"Authorization": f"Bearer {key}"}) as ws:
        await ws.send(json.dumps({"type": "start", "call_id": "test-1"}))
        with wave.open(path) as w:          # 8 kHz, mono, 16-bit
            while chunk := w.readframes(800):
                await ws.send(chunk)
                await asyncio.sleep(0.1)    # real time, like a live call
        await ws.send(json.dumps({"type": "close"}))
        return json.loads(await ws.recv())

print(asyncio.run(detect("call.wav", "amd_live_...")))

A health check: send "call_id": "__healthcheck__" with 2 s of silence. It is never billed or recorded, and should return SILENCE.

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.