# Raw WebSocket

If you control the media (FreeSWITCH, Asterisk, LiveKit, Pipecat, your own SIP stack), open a socket and send binary audio frames. Decision events come back on the same socket.

```
wss://api.patchamd.com/v1/stream?encoding=mulaw&sample_rate=8000
Authorization: Bearer pamd_live_…
```

| Query | Values |
|---|---|
| `encoding` | `mulaw`, `alaw` or `linear16` (16-bit little-endian PCM) |
| `sample_rate` | `8000` (default), `16000`, `24000`, `44100`, `48000` |
| `transcripts` | `1` to also receive `transcript` events with the words heard so far |
| `webhook_url` | optional: also send `amd.decided` there |

Can't set headers? Mint a URL with `POST /v1/streams` and `"encoding": "mulaw"` — the returned `url` carries the token and the codec.

## Protocol

1. Connect **when the call is answered**. The first audio frame starts the clock.
2. Send the callee's audio only (not your own outbound audio), ideally in 20 ms frames, in real time.
3. Receive events:

```json
{"type":"connected","detection_id":"det_…"}
{"type":"started","detection_id":"det_…","carrier":"raw","encoding":"mulaw/8000"}
{"type":"transcript","detection_id":"det_…","text":"Hello?","finished":true}
{"type":"decision","detection_id":"det_…","result":"human","reason":"person_waiting","at_ms":1620,"heard":"Hello?"}
```

4. After `decision` the server closes the socket with code 1000. To stop early (the callee hung up), send `{"type":"stop"}` or close the socket; nothing is billed without a decision.

## Example (Node)

```js
import WebSocket from 'ws';

const ws = new WebSocket('wss://api.patchamd.com/v1/stream?encoding=mulaw&sample_rate=8000', {
  headers: { authorization: `Bearer ${process.env.PATCH_AMD_KEY}` },
});
ws.on('open', () => call.on('audio', frame => ws.send(frame)));
ws.on('message', data => {
  const event = JSON.parse(data);
  if (event.type === 'decision') route(event.result);
});
```

## Close codes

| Code | Meaning |
|---|---|
| 1000 | Decided |
| 4401 | Bad credentials in a Twilio `start` message |
| 4408 | No audio within 90 s of connecting |
| 4429 | Concurrent stream limit reached |

Bad credentials on connect are rejected before the upgrade with HTTP 401; a reused token with 409; rate limits with 429.
