ByeBeep docs

Answering-machine detection, as an API

ByeBeep tells you, while the call is still young, whether a person or a machine answered: a voicemail, a fax, a network message, a call screener, or nobody at all. A person goes to your agent as soon as they finish their hello. A machine is let go at 2 seconds and never later than 5: no sooner, because a shorter call saves nothing on 6-second billing and very short calls hurt your traffic; no later than the AMD you have today.

  • Drop in: a one-line install for VICIdial and Asterisk, a media-stream URL for Twilio, SignalWire and Telnyx, mod_audio_fork for FreeSWITCH, or a WebSocket from anything else.
  • Safe: on VICIdial and Asterisk, if ByeBeep cannot answer in time the call carries on with your existing AMD. It never sits in the SIP path.
  • Clear results: human, machine or unsure, a subtype, a confidence, how long it took and why, and the voicemail beep when you want to leave a message.
  • Never in a hurry to hang up: a call screener ("If you record your name and reason for calling…") is held to the end of the window, because a person may still pick up.
  • Pay per detection: from $0.06 per 1,000, 100,000 free, and a free 30-day shadow trial.

Quickstart (30 seconds)

  1. Create an account. You get a free test key (bb_test_…) at once, no card needed.
  2. Send a recording of the called party, from the moment of answer. No recording? Use our sample voicemail (Asterisk prompt by Allison Smith, CC BY-SA 3.0).
curl https://api.byebeep.com/v1/detect \
  -H "Authorization: Bearer bb_test_YOUR_KEY" \
  -F audio=@voicemail.wav
{
  "verdict": {"class": "machine", "subtype": "voicemail", "confidence": 0.92,
              "decided_ms": 2000, "sure_ms": 320, "decided_by": "fingerprint"},
  "audio_ms": 5654, "session_id": "895f47f8b70ebf4978c0744d", "billable": false, "test": true
}

For live calls, stream the audio instead (below): the verdict then arrives in the first seconds of the call, not after it.

Rather click than type? The portal's Playground streams the sample, your microphone or a recording to the live API and marks the verdict on the waveform, with the exact messages your code would send and receive.

VICIdial

On each dialer, as root:

curl -fsSL https://api.byebeep.com/install.sh | sudo bash -s -- --key bb_live_YOUR_KEY

The installer checks Asterisk, installs the connector, and puts ByeBeep in front of AMD() on VICIdial's AMD routes (8369, 8373, 8375), keeping AMD() behind it as the fallback. It also runs a small relay that keeps connections to the API open, so a call never waits for a handshake. Then it runs a self-test. Your campaigns and AMD Status Map work as before: ByeBeep sets the same variables.

VariableExample
AMDSTATUSHUMAN, MACHINE, NOTSURE
AMDCAUSEVOICEMAIL-480-0.97-BYEBEEP (subtype, ms, confidence)
BYEBEEP_SESSION, BYEBEEP_BEEP_MSfor logs and voicemail drop

Timing. A person is passed on the moment ByeBeep is sure. A machine is let go no sooner than min_ms (2000) and a call screener at max_ms (5000); both live in /etc/byebeep/connector.conf, per dialer. Set min_ms = 0 to hang up the moment it is sure. A call still unsure at max_ms goes to an agent, as NOTSURE always has.

Leaving voicemail? Install with --beep: on a voicemail the connector waits for the beep (20 s at most) before VICIdial plays your message. Undo: --uninstall restores your dialplan from the backup it made.

Late correction (Plus). Install with --late event or --late hangup. After a "person" verdict the call goes on at once, and the relay keeps listening for 6 seconds. If what follows turns out to be a voicemail (the greeting carries on, a known greeting, a beep), it sets BYEBEEP_CORRECTION (machine), BYEBEEP_CORRECTION_REASON and BYEBEEP_CORRECTION_MS on the channel and writes a line to the connector's log; with hangup it also hangs the call up, but only when the reason is fingerprint (a greeting we know). BYEBEEP_LATE says whether it is watching (watching, or unavailable without the relay). Add webhook = yes to /etc/byebeep/connector.conf to get every event, corrections included, at your webhook as well. How far to trust each reason, measured on real calls: fingerprint flagged no people; greeting_continues and beep also fire on about 1 in 25 people (background noise, a tone on the line), so they are reported and never hung up on. Treat them as a hint.

Asterisk

Any Asterisk 16+ can use the same connector as an EAGI program. Install it with the line above, then call it where you want detection:

exten => s,n,EAGI(byebeep)                          ; sets AMDSTATUS / AMDCAUSE
exten => s,n,GotoIf($["${AMDSTATUS}" != ""]?done)
exten => s,n,AMD()                                    ; your fallback
exten => s,n(done),NoOp(${AMDSTATUS} ${AMDCAUSE})

Arguments: EAGI(byebeep,beep), mode=fast|balanced|accurate, max_ms=N, min_ms=N.

FreeSWITCH

With mod_audio_fork, fork the answered leg with the start message as metadata. Verdicts come back as mod_audio_fork::json events.

uuid_audio_fork <uuid> start wss://api.byebeep.com/v1/stream mono 8k {"type":"start","key":"bb_live_YOUR_KEY","encoding":"pcm16","sample_rate":8000}

Twilio and SignalWire

Start a media stream of the called party's audio when the call is answered. Verdicts go to your webhook with Twilio's CallSid as call_id.

<Response>
  <Start>
    <Stream url="wss://api.byebeep.com/v1/twilio" track="inbound_track">
      <Parameter name="key" value="bb_live_YOUR_KEY"/>
    </Stream>
  </Start>
  <Pause length="10"/>
</Response>

Optional parameters: mode, max_ms, min_ms, beep (true). Anything else you add comes back in the webhook's metadata. SignalWire speaks the same protocol at the same address.

Telnyx

Start media streaming on the answered call with stream_track: inbound_track and

stream_url: wss://api.byebeep.com/v1/telnyx?key=bb_live_YOUR_KEY

Verdicts go to your webhook with the call_control_id as call_id.

Any platform (WebSocket)

Open a WebSocket when the call is answered, send a start message, then the called party's audio as binary frames of 20–100 ms. Verdicts come back on the same socket.

// Node 22+, no dependencies
const ws = new WebSocket('wss://api.byebeep.com/v1/stream');
ws.onopen = () => ws.send(JSON.stringify({ type: 'start', key: 'bb_live_YOUR_KEY', encoding: 'mulaw', sample_rate: 8000 }));
ws.onmessage = (m) => { const ev = JSON.parse(m.data); if (ev.type === 'verdict') console.log(ev.class, ev.subtype, ev.decided_ms); };
// then, for each 20 ms of audio from the call:  ws.send(frame);  and at hang-up:  ws.send('{"type":"stop"}')

Streaming API

wss://api.byebeep.com/v1/stream. Authenticate with the header X-Api-Key: bb_live_… (or Authorization: Bearer …); clients that cannot send headers put "key" in the start message.

Start fieldValuesDefault
encodingmulaw, alaw, pcm16mulaw
sample_rate8000, 160008000
modefast, balanced, accuratebalanced
max_ms1000–10000: the latest a verdict may come5000
min_ms0–max_ms: the earliest a machine is reported. ByeBeep keeps listening until then; a call screener waits until max_ms. People are never held. 0 = report a machine the moment it is sure2000
options.beepreport the voicemail beep after a machine verdictfalse
options.late_correctionreport a voicemail heard after a person verdictfalse
options.webhookalso send events to your webhookfalse
call_id, metadatayour references, echoed back

Send {"type":"stop"} or close when the call ends; ByeBeep decides on what it heard. A stream with no audio for 3 s is reported as no_audio and not billed.

Events

TypeWhenFields
readyafter the start messagesession_id
verdictas soon as one is reachedclass (human, machine, unsure), subtype, confidence, decided_ms, decided_by; sure_ms when a machine was held for min_ms (the moment it was already sure); announcement_ms when a network announcement played first
beepthe voicemail beep, if asked forbeep_ms
correctiona person verdict turned out to be a voicemail, if asked for. reason: fingerprint (a greeting we know; reliable), greeting_continues or beep (hints: people trigger them too)class, at_ms, reason
endthe stream is finished

Subtypes: person, voicemail, screener (a call screener or a phone's own assistant: "If you record your name and reason for calling…", "I'm a call assistant…"), fax, sit (a network message such as "number disconnected"), silence, ringback, announcement, no_audio. Treat unsure as a person, as VICIdial does.

Network announcements. Some carriers play a notice before the call connects onward, such as "Your call has been forwarded", which is followed either by voicemail or by a person picking up. ByeBeep recognises known announcements and judges the call from where they end, so a person who answers after one is not taken for a machine. The verdict then carries announcement_ms; a call that ends (or reaches max_ms) during one is unsure / announcement.

POST /v1/detect

One recording in, one verdict out: for trying ByeBeep and for recordings. Send a form field audio (WAV: 16-bit PCM at 8 or 16 kHz, or G.711) or a raw body with ?encoding=mulaw&sample_rate=8000. Optional: mode, max_ms, min_ms, beep=true, call_id. Up to 4 MB. Billed like a streamed call.

Webhooks

Set an https address in the portal (Keys page). We POST each event as JSON with session_id, call_id, platform and metadata, signed:

X-ByeBeep-Signature: t=1794600000,v1=5f2c…
expected = hex(HMAC_SHA256(your_secret, t + "." + raw_body))
# compare v1 with expected in constant time; reject if t is more than 5 minutes old

Answer 2xx within 5 seconds. We retry twice (after 1 s and 5 s) on errors and timeouts, not on 4xx. The portal can send a test event.

SDKs

Small clients for Python (asyncio), Node 22+ (no dependencies) and Go: open a stream for a call, send audio, receive events, detect a WAV file in one call, and verify webhook signatures.

# Python
import asyncio, byebeep
print(asyncio.run(byebeep.detect_wav("bb_test_YOUR_KEY", "voicemail.wav")))

// Node
import { detectWav } from './byebeep.mjs';
console.log(await detectWav('bb_test_YOUR_KEY', 'voicemail.wav'));

They are being published; until then write to team@vicifast.com and we send them.

Errors and limits

WhatAnswer
Wrong or missing keyHTTP 401, or the socket closes with invalid or missing API key
Over your account's calls at oncecloses with this account is at its concurrent-call limit (the VICIdial connector falls back to AMD())
Test key over 200 a day or 2 at onceHTTP 429 or a close saying so
A bad start messagean error event naming the field, then a close

Keys and logs

Test keys (bb_test_…) come at sign-up and work at once: free, 200 detections a day and 2 calls at once per account. Live keys (bb_live_…) come once your email is confirmed and a card is saved. Every key is shown once; we keep only a fingerprint of it. Revoked keys stop working within 15 seconds.

Logs. The portal's Logs page lists every detection for 30 days (time, key, call ID, verdict, when and how it was decided), filterable by key, verdict, or call or session ID. New calls appear within about 15 seconds.

Pricing

Monthly volumeCorePlus
First 250k$0.30 per 1,000$1.50 per 1,000
250k–1M$0.12$1.50
1M–10M$0.06$1.10
Over 10M$0.06$0.75

Core: verdict, subtype, timings, webhooks. Plus adds beep timing and late correction. 100,000 detections free once; the first 30 days after you go live are a free shadow trial; a month with any charge is at least $29. Monthly invoices, charged to your card. Calls with no audio are free.

Data and privacy

The API does not store call audio. For each call we keep a usage record (time, key, call ID, verdict, timings, session ID) for billing and support; your Logs page shows it for 30 days. Audio reaches us only as the stream of the called party's side, over TLS, and is processed in memory.