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, or nobody at all. A voicemail greeting it has heard before is recognised in about half a second, others within a few seconds; a person as soon as they finish their hello.

  • 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.
  • 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": 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

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.

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.

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, 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 come4000
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, and 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 forclass, at_ms, reason
endthe stream is finished

Subtypes: person, voicemail, 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, 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, 2 calls at once. 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.