Read call evidence

Recordings, waveform peaks, transcript turns, summaries, and retention/disclosure state for every call.

Real browser and phone calls can attach evidence to the session. An agent reads available evidence with the call and recording tools. Failed calls and offline synthetic fixtures need not have real audio; check recording state rather than assuming every call has a recording.

The evidence tools

→ list_calls(...)                        # calls:read
→ get_call(call_id="cl_...")             # calls:read — status + recording state
→ get_call_recording(call_id="cl_...")   # recordings:read — metadata + disclosure
→ GET recording.download_url              # recordings:read — playable audio bytes
→ get_call_transcript(call_id="cl_...")  # recordings:read — transcript turns

What a recording contains

A recording is the call's audio metadata plus:

  • Waveform peaks — for rendering a scrubbable waveform.
  • Transcript turns — speaker-attributed turns of the conversation.
  • Retention / disclosure state — how long the audio is kept and whether the required call disclosure was made.

get_call returns the call status and recording state; get_call_recording returns recording metadata, disclosure state, and a relative download_url; get_call_transcript returns the transcript turns. Fetch download_url with the same bearer credential to play or download the audio. The endpoint accepts one standard Range: bytes=... header, so browser audio players can seek and resume with 206 Partial Content; an invalid or multipart range returns 416.

The downloaded full-call recording is a listening mix. OpenPhonex balances the caller and agent from speech-bearing sections with bounded gain and shared peak headroom, so a quiet participant is easier to hear without amplifying a silent or noise-only track. This processing does not change timing or transcript turn boundaries. The separate captured caller and agent tracks remain unchanged as the source evidence; they are storage inputs, not additional public download URLs.

Do not construct a URL from storage_key or media_url—those fields describe storage internals and are not a stable download contract. Recording reads require the recordings:read scope, which is distinct from calls:read so you can grant transcript access without call metadata, or vice versa.

Retention

Recording retention runs on the OpenPhonex side (dry-run or execute). The disclosure state on each recording tells you whether the call was announced as recorded — surface it when you summarize a call back to a human.

The call event log

GET /v1/calls/{call_id}/events returns one bounded page of the call's own event rows, oldest first, each with its raw payload. It is the platform's internal bookkeeping log rather than a webhook replay, so it contains types that are never delivered to any webhook endpoint. Reading it needs calls:read.

call.wrong_party_signals

Written once when a call ends on a wrong-party verdict — the caller said they are not the person the agent asked for. That verdict comes from one decision model reading one caller reply and it ends the call, so this event records how much evidence it actually rested on. It is written for both routes to that outcome: the identity step's own wrong_party route, and the mid-call interrupt:wrong_party global interrupt.

FieldMeaning
route_idWhich path carried the verdict: wrong_party (the identity step's own route) or interrupt:wrong_party (the global interrupt).
from_step_idThe workflow step the verdict was reached from.
turn_indexThe caller turn the verdict landed on. 1 is the caller's first reply.
utterance_charactersNumber of characters in that reply, after trimming whitespace.
utterance_wordsNumber of whitespace-separated words in that reply.

turn_index is -1 when the turn count could not be read, and never 0 for that case, because 0 is a real turn index: an unreadable turn must not be counted among the verdicts decided on the caller's first reply, which is the number this event exists to measure.

The caller's words are not in the payload

Only the two sizes are recorded, never the utterance itself. The transcript is where speech belongs — read it with get_call_transcript. A short reply on turn 1 is the signal that a wrong-party verdict was thin; pair it with the transcript when you want to know what was actually said.

This event is readable here but is not part of the webhook catalogue, so it is never delivered to a webhook endpoint. Poll the events endpoint for it, or read it alongside the rest of a call's evidence.

Listen turn by turn in Calls

The Calls workspace keeps the mixed recording player and can also show a compact play control beside a transcript turn. It shows that control only when the bounded Calls response marks the turn has_audio; older text-only imports and turns without an observed clip do not get a broken player.

The browser never receives a recording bucket URL or an artifact identifier. OpenPhonex resolves the linked clip through the signed-in workspace session, after applying the same recording-consent, retention, and deletion checks as the mixed recording. Deleting a recording removes turn playback as well; it does not cause historical audio to be regenerated or retranscribed.

Where a call's telemetry lives

A call's timing and cost evidence — the per-turn latency budget, the cost breakdown, and which figures were measured rather than estimated — is part of the call, so it is read on the call. Open a call in Calls and pick the Performance tab of the record that slides open beside the table.

The open record and the tab are both in the URL, so a performance view is a link you can send or bookmark:

https://openphonex.com/calls?call_id=cl_...&tab=performance

The former standalone /calls/performance page is gone. It now redirects to the Calls table, carrying a call_id through to the Performance tab of that call, so older links and bookmarks keep working. Nothing about the evidence itself changed, and the API is unaffected: the tab reads the same telemetry the page read.

Carrier-leg call quality

A recording is decoded after the browser-grade jitter buffer of the recorder, so it can sound clean while the phone on the other end heard something robotic. For a call carried by the OpenPhonex SIP bridge, the call's evidence now also carries the bridge's own per-call counters for the leg between the bridge and the carrier, so that case can be told apart from a problem inside the platform without a packet capture.

Open the call in Calls and expand Technical details. Five rows sit beside RTP receive, each named by the direction the packets travelled:

  • Carrier → SIP bridge — the negotiated codec, the direction, whether the bridge's jitter buffer was on, and what the bridge received from the carrier: packets, sequence gaps, late and delayed packets, jitter-buffer loss and the maximum per-packet latency.
  • SIP bridge → carrier — the audio frames the bridge encoded and sent toward the carrier, and its maximum output latency.
  • Room → SIP bridge — the AI agent's audio as the bridge received it from the media room: packets, gaps, late and delayed packets (with the total delayed time), jitter-buffer loss and reconnects. Delayed intervals between the agent's utterances are normal; delayed or late packets during an utterance are not.
  • SIP mixer — whether the bridge paced its output on time: mixes, on-time mixes, timing jumps and dropped input frames.
  • Carrier reception (RTCP) — the carrier's own reception report, kept as a separate row on purpose: the four rows above are measured at the bridge and cannot show what the carrier received. This row reads Not captured today (the SIP bridge does not read RTCP). Even when captured, a receiver report describes loss and jitter at the carrier's receiver only; it cannot clear the path beyond the carrier to the handset or detect every audible distortion.

GET /v1/calls/{call_id}/observability returns the same projection as dashboard_summary.media_evidence.sip_call_statistics, with the sections carrier_to_bridge, bridge_to_carrier, room_to_bridge and mixer, plus direction, audio_codec, jitter_buffer_enabled, logged_at and the platform's own livekit_sip_call_id (an opaque reference OpenPhonex support can correlate with the carrier leg). Its status is complete, partial (a section is missing and reason names it) or unavailable with a reason such as not_captured for a call recorded before this evidence existed. The carrier's report is the separate media_evidence.carrier_reception group (status, reason, source: carrier_rtcp), unavailable / rtcp_not_captured today. A counter the bridge did not report is absent rather than zero, and a group the capture never saw reads as not captured, never as a clean leg or zero loss.

Inbound attempts rejected before connection

An incoming call can reach OpenPhonex but be rejected before the AI agent answers. When the called number identifies your workspace, the rejected attempt appears in Calls with its time, direction and a customer-readable reason. Repeated delivery of the same carrier call does not create another attempt.

For example, missing pricing for a managed number is a configuration problem, not evidence that the caller hung up or that the destination was busy. Follow the explanation on the call or the number's Activation checks. An attempt rejected before billable media starts has no conversation recording or transcript to play; do not interpret missing audio as a recording failure.

Only attempts attributable to your workspace appear in its history. Calls to unknown numbers are not assigned to a customer workspace.

On this page