answering machine detection

Constructing a Node.js SIP pipeline for real-time stream AMD

Pipe live PCM audio directly from a custom Node.js dialer into a WebSocket endpoint to classify answers in 1/8 second.

By Ayesha Kazi·September 16, 2026·4 min read
What matters here
  1. Streaming PCM audio over WebSockets enables answering machine classification within 125 milliseconds.
  2. Real-time stream analysis catches carrier false answer supervision before agents waste time on dead calls.
  3. Custom Node.js dialers bypass legacy silence timers, cutting ghost calls and protecting caller reputation.

Building a custom outbound dialer in Node.js gives engineering teams direct control over call routing and agent dispatch. However, handling call progress detection in JavaScript is notoriously difficult. Legacy engines rely on static silence duration and energy spikes. These legacy systems misclassify 10% to 20% of live answers as machines, dropping real human leads, creating ghost calls, and burning prospect lists.

Modern dialer architectures decouple media analysis from call control. Instead of relying on local silence thresholds or heavy DSP modules, you stream raw audio from your Node.js media server directly into AMDY over a WebSocket connection. This architecture provides fast, accurate call classification without overloading your core dialer application.

Architecture: Streaming PCM from Node.js

When building a custom outbound pipeline, your media engine receives audio packets as soon as a SIP channel connects. Traditional dialers wait several seconds to measure audio bursts and silent pauses. Our guide on how to replace stock ViciDial AMD with stream audio detection explains why standard silence rules fail on modern mobile networks.

In a Node.js stack, your application taps the incoming RTP stream immediately upon answer. You establish a persistent WebSocket socket to the AMDY API endpoint. As raw linear PCM audio buffers fill—typically in 20ms to 50ms frames—your Node.js process writes these binary buffers directly into the socket connection.

Opening the Socket and Handling Audio Streams

An outbound pipeline should open the WebSocket session during SIP setup or immediately upon receiving a 200 OK answer signal. AMDY begins analyzing audio fingerprints within 1/8 of a second (125ms) of audio stream arrival.

To establish the stream, instantiate a standard WebSocket client in Node.js using the ws library. Pass your call session identifier in the query parameters or handshake headers. This maintains channel mapping across clustered application instances.

Handling Real-Time Classification and Carrier FAS

Legacy silence detection requires three to five seconds to distinguish human speech from recorded answering machine greetings. This delay creates dead air and breaches regulatory abandonment limits. As detailed in our breakdown of how AMD latency triggers TCPA abandonment rules, slow classification causes dialers to breach strict regulatory caps.

Because AMDY achieves 99% accuracy within 1/8 of a second, your Node.js application gets a classification payload before the target finishes their initial greeting. The socket emits JSON events identifying the call status as human, machine, or carrier false answer supervision (FAS).

Trapping Carrier False Answer Supervision

Carrier FAS is a persistent problem in outbound dialing. Downstream carriers send a SIP 200 OK signal while returning artificial ringback, dead air, or line noise, charging dialers for unconnected calls. Because AMDY analyzes the live audio stream immediately following the 200 OK signal, it detects FAS patterns instantly. When your Node.js application receives a FAS classification, it drops the channel immediately. This stops false connection charges and keeps agents off empty lines.

Step-by-Step Implementation Workflow

Integrating AMDY into a Node.js dialer pipeline follows a straightforward sequence:

  1. Initialize the Outbound Call: Your Node.js application triggers a SIP INVITE through your softswitch or media gateway.
  2. Attach Media Tap: Upon receiving a 200 OK or early media signal, tap the incoming RTP stream and format it as 8kHz or 16kHz linear PCM audio.
  3. Open WebSocket Socket: Connect to the AMDY WebSocket API, sending session tokens and unique call tracking IDs in the header.
  4. Stream Binary PCM Chunks: Write incoming audio buffer chunks straight to the WebSocket stream as binary payloads.
  5. Execute Routing Decision: Handle the returning JSON decision event. If classified as human, bridge the live audio to an available agent immediately. If classified as machine, initiate an automated voicemail drop or disconnect the call.

Engineering Trade-Offs and System Limits

Building an outbound dialer with a WebSocket audio pipeline offers distinct advantages over built-in silence modules, but it requires specific design decisions.

  • Network Bandwidth vs CPU Load: Local silence detection processes audio on the local host, consuming local CPU cycles. Offloading audio analysis to an external WebSocket endpoint trades local compute load for network bandwidth. Your media servers must maintain low-latency network connections to handle concurrent audio streams.
  • Node.js Event Loop Management: While Node.js handles asynchronous socket operations well, transforming heavy audio formats inside JavaScript can block the single-threaded event loop. Perform media re-encoding in C++ native add-ons or media proxies prior to piping PCM data into Node.js.
  • Integration Speed: AMDY offers a WebSocket API for custom dialers, alongside native installation support for standard platforms like ViciDial, Asterisk, FreeSWITCH, GoAutoDial, FreePBX, Issabel, and 3CX. Teams can start testing with 50,000 free detections upon sign-up to evaluate stream performance.

Decoupling audio classification from local silence algorithms protects your dialer from false positives, eliminates dead air, and maximizes live agent connection rates.

More from AMDY.IO News