Skip to main content
PyAI AMD tells your dialer who or what answered: a person, a voicemail, an IVR, an iPhone/Google screening assistant, a dead number, and more. Each class is a different next move. Every decision includes the reason. In-region, a human lands under 300 ms and a machine under 800 ms (early measurement, not an SLA), inside Twilio’s documented 2.5 to 4 second dwell. Already on Twilio? One line of TwiML.
Endpoint: wss://api.pyai.com/v1/amd/stream (scope amd:detect). Config: POST /v1/amd/config (scope amd:configure). Reads: GET /v1/amd/calls, GET /v1/amd/calls/{id} (scope amd:read). AMD meters answered calls. Current rates and included usage are on the pricing page.
An instant sandbox key includes amd:detect, amd:configure, and amd:read, so you can test the WebSocket, config, and call-record flow without billing.

The wedge: a zero-code Twilio migration

AMD speaks Twilio’s Media Streams protocol natively (start / media / stop frames, G.711 μ-law 8 kHz base64), so a customer already on Twilio adds one line of TwiML, no carrier change, no new SDK, to fork the call’s media to PyAI:
Use <Start><Stream>, not <Connect><Stream>. <Start> forks a copy of the audio and TwiML execution continues immediately to your next verb, so the call still goes where it was going. <Connect> hands the call’s media path to the socket and blocks TwiML until the stream ends — and because AMD is listen-only and never sends audio back, the caller would hear dead air and your dialer would never reach the agent. (<Connect> is right for Omni, which is a two-way voice agent. AMD only listens.)Keep Twilio for the call itself; drop machineDetection from the call and let AMD make the decision instead.
Twilio cannot set headers, WS subprotocols, or even URL query parameters on <Stream> (the query string is stripped before connecting), so the key travels as a <Parameter>: it arrives in the stream’s start frame and PyAI verifies it before processing any audio. A connection that never presents a valid key is closed within seconds. Your own server-side clients (not Twilio) can authenticate at the handshake instead, with ?api_key= on the URL or the Sec-WebSocket-Protocol: pyai.v1, pyai-key.<API_KEY> subprotocol.

The decision

PyAI pushes an amd event mid-call (and to the per-call TwiML webhook, if you set one) so a predictive dialer can route or drop instantly:
A machine decision is further classified by subtype, voicemail, ivr, screening (iPhone/Google Call Screen), or music (hold music). The subtype is stored on the call record rather than pushed on the wire: read it back with GET /v1/amd/calls/{id} (or on the amd.call.completed webhook below), where answered_by is one of human, machine, voicemail, ivr, screening, music, sit_invalid, unknown.

Webhooks

Two webhook paths, carrying different payloads:
  • Per-call, the TwiML <Parameter name="webhook">. The moment the decision lands, PyAI POSTs the wire event above (the coarse answered_by class) to that URL, for that call only.
  • Account-wide, webhook_url in POST /v1/amd/config. When the call completes, PyAI POSTs a signed amd.call.completed event with the full call record, including the machine subtype in answered_by:
Most dialers ignore the socket entirely and drive everything from webhooks. For the account-wide completion webhook, verify X-PyAI-Signature: t=<unix_seconds>,v1=<hex> against the exact raw body:
Reject stale timestamps and deduplicate retries by call/event id. Mint or rotate the organization secret with POST /v1/webhooks/signing-secret. The per-call TwiML webhook is a separate low-latency callback; do not assume it has the same full-record payload as amd.call.completed.

The one dial: aggressiveness

AMD has a single operating-point dial, aggressiveness ∈ [0, 1], set per account (POST /v1/amd/config) or per call (a TwiML <Parameter>):
  • 0.0-0.25, human-safe (default). Never hang up on a person. For predictive dialers with live agents; on the deadline it returns unknown (let the agent listen) rather than risk a false machine.
  • 0.6-1.0, machine-aggressive. Fire machine fast, for AI voicemail-drop bots.
The error costs are asymmetric, a false machine (hang up on a prospect) is far worse than a false human (waste a few agent-seconds), so you pick your point on the curve instead of living with one fixed default.

Set it up with the SDK

Two ways to turn it on

  1. Twilio drop-in, the TwiML above. Keep your carrier and your code; the answered_by_twilio field speaks Twilio’s exact vocabulary.
  2. Native, a flag on any call already running on PyAI telephony or Omni. See the pricing page for its current treatment.

Billing

AMD records one amd.calls unit for each answered call. Reconcile usage from the call record and x-pyai-units; current rates, included usage, and any bundling are listed on the pricing page. A fast decision also means fewer dead-air carrier seconds on every answered call.

Troubleshooting