Capture your calls
Separate channels, scored offline, whatever produced them. A frozen recording re-scored in CI proves the evidence and scorer are intact; proving today’s agent needs a new capture, not a replay.
The recording you capture feeds a five-dimension conversation-test: separated caller and agent channels are what the Conversation (turn-taking) and Speech (timing) dimensions read, and the same file drops into hotato test run beside a transcript and trace for the Outcome and Policy checks.
| Stack | Automatic pull | Separate channels | Contract-grade default | Network use |
|---|---|---|---|---|
| Vapi | Yes: capture fetches by call id | Yes, out of the box | Yes | One call: fetch the recording |
| Twilio | Yes: capture fetches by recording SID | No by default: must request RecordingChannels=dual at creation | No, opt-in | One call: fetch the recording |
| Retell | Yes: capture fetches by call id | Yes, once call recording is turned on | Opt-in: enable recording first | One call: fetch the recording |
| LiveKit | No: egress runs in your own infra | Yes, if egress records both tracks | Opt-in: configure egress for two tracks | None from Hotato |
| Pipecat | No: the setup recorder runs in your pipeline | Yes, the scaffold writes caller ch0 / agent ch1 | Yes, once the recorder is added | None: local recorder, no network call |
Automatic pull: does hotato capture fetch it for you. Separate channels: is caller/agent separation what you get. Contract-grade default: is that separation on without extra setup. Network use: what capture calls out to, not your infrastructure.
The two-channel requirement
Per-channel energy: separation reveals who talked over whom.
- channel 0 = caller, channel 1 = agent (override:
--caller-channel/--agent-channel). - Dual-channel / stereo / separate-track recording: Vapi ships it by default; Twilio and LiveKit need it requested at record time; Retell and Pipecat need the recorder turned on. Exact status per stack in the table above.
- Mono-mixed sums both into one waveform: overlap cannot be attributed.
hotato runrejects it by default. Two opt-in flags score it anyway, indicative only, never equivalent to dual-channel:--allow-monooncapture/pull, and--diarizeonrun(separates the mix via speaker diarization, confidence-gated, refuses below the bar).
run or capture scores it fully. One mixed mono file: the scorer can’t tell who was talking, so run rejects it by default; --allow-mono (on capture/pull) or --diarize (on run) is the only way in, at reduced confidence.Three ways in
A. Let Hotato pull one recording
Vapi, Twilio, Retell: capture auto-pulls the recording (the only network call) and scores offline: Vapi · Twilio · Retell.
LiveKit and Pipecat: setup prints a recording scaffold; record in your own infra, then score the file. Scoring many calls? hotato analyze ranks every WAV in a local folder, the same dashboard sweep produces for a connected stack.
# auto-pull a Vapi call and score it (needs VAPI_API_KEY) uvx hotato capture --stack vapi --call-id <id> # no key? prove the capture → score loop on a bundled call uvx hotato capture --stack vapi --demo
B. Score a recording you already have
Have the WAV? Caller on channel 0, agent on channel 1:
uvx hotato run --stereo call.wav --stack generic --expect yieldC. Connect once, pull recent calls
connect stores a stack’s credentials once, locally (~/.hotato/connections.json, mode 0600). pull bulk-fetches recent recordings by the vendor’s list endpoint; sweep pulls, then ranks them in one offline dashboard. Vapi and Twilio have a verified list endpoint; Retell needs explicit --call-id.
# connect once, then see candidate turn-taking moments across recent calls uvx hotato connect vapi --api-key <key> uvx hotato sweep --since 7d
Five mono stacks pull too, behind --allow-mono: Bland, ElevenLabs, Synthflow, Millis, and Cartesia. One combined track, no per-party channel: capture/pull score it only in degraded mode: indicative only, no contract-grade talk-over attribution. sweep can’t rank it at all; whole-call scanning needs two channels, so pulled mono calls land in the dashboard’s skipped list, not scored.
Every adapter and its limits
What each stack gives Hotato to score. Per-adapter verification status, full detail, and endpoints: ADAPTER-STATUS.md.
| Stack | Channels | Single call (capture) | Bulk (pull / sweep) |
|---|---|---|---|
| Vapi | dual, separated | auto-pull by --call-id | list endpoint, --since / --limit |
| Twilio | dual, separated | auto-pull by --recording-sid | list endpoint, --since / --limit |
| Retell | dual, separated (multi-channel URL) | auto-pull by --call-id | no list endpoint: explicit --call-id only |
| LiveKit | dual, separated | capture-in-your-infra (setup) | not connectable: no vendor recording to list |
| Pipecat | dual, separated | capture-in-your-infra (setup) | not connectable: no vendor recording to list |
| Bland | mono only | --allow-mono, indicative | --allow-mono, list endpoint |
| ElevenLabs | mono only | --allow-mono, indicative | --allow-mono, list endpoint |
| Synthflow | mono only | --allow-mono, indicative | --allow-mono + --model-id, list endpoint |
| Millis | mono only | --allow-mono, indicative | --allow-mono, list endpoint |
| Cartesia | treated as mono | --allow-mono, indicative | --allow-mono + --agent-id, list endpoint |
Per-stack guides
Hotato’s scorer needs separated caller and agent tracks: one two-channel WAV or two aligned mono WAVs. If your stack only exports one mixed mono file, switch on separated recording first; each guide above shows the setting.