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:
<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 anamd 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 coarseanswered_byclass) to that URL, for that call only. - Account-wide,
webhook_urlinPOST /v1/amd/config. When the call completes, PyAI POSTs a signedamd.call.completedevent with the full call record, including the machine subtype inanswered_by:
X-PyAI-Signature: t=<unix_seconds>,v1=<hex> against the exact raw body:
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 falsemachine. - 0.6-1.0, machine-aggressive. Fire
machinefast, for AI voicemail-drop bots.
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
- Twilio drop-in, the TwiML above. Keep your carrier and your code; the
answered_by_twiliofield speaks Twilio’s exact vocabulary. - Native, a flag on any call already running on PyAI telephony or Omni. See the pricing page for its current treatment.
Billing
AMD records oneamd.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.