Skip to main content

Calls

Every call leaves a row, written at answer time rather than at the end — a call that drops mid-way is exactly the one worth investigating, and writing only on completion loses it.

List calls​

GET /api/voice/calls?call_type=outbound&outcome=completed&limit=50

ParamNotes
call_typeinbound or outbound
outcomeSee outcomes
sentimentpositive, neutral, negative
flow_uuidRestrict to one flow
date_from / date_toRFC 3339 or YYYY-MM-DD; a bare date_to covers the whole day
searchMatches customer name, from_number, or to_number
limit / offsetDefault 50 / 0
{
"calls": [
{
"uuid": "cal-c1d2e3f4-a5b6-7890-abcd-ef1234567890",
"call_type": "outbound",
"from_number": "+915550010000",
"to_number": "+918888888888",
"duration": 113,
"duration_formatted": "1:53",
"tts_provider": "sarvam",
"outcome": "initiated",
"sentiment": null,
"summary": null,
"key_details": null,
"recording_url": "https://…/recording.mp3",
"started_at": "2026-09-20T14:32:01Z",
"ended_at": "2026-09-20T14:33:54Z",
"created_at": "2026-09-20T14:32:01Z",
"customer_name": "Priya Kumar",
"flow_name": "September winbacks",
"agent_name": "Aria"
}
],
"total": 891
}
Joined names are omitted when absent

customer_name, flow_name, and agent_name are joined from related rows and use omitempty — when there is no linked customer, flow, or agent the key is absent from the object entirely, not present as null. An inbound call from an unknown number has no customer_name; a human click-to-call has no agent_name unless one was recorded. Read them defensively.

Which number is whose

from_number and to_number are literal call legs, not roles. On an outbound call from_number is your own number and to_number is the customer's; on an inbound call it is the other way round. A UI showing "the customer" must branch on call_type.

Get a call​

GET /api/voice/calls/{uuid}

Returns the full record including the transcript, which the list endpoint omits to keep pages small:

{
"uuid": "cal-c1d2e3f4-…",
"transcript": [
{ "speaker": "agent", "text": "Hi, is that Priya?", "ts": "2026-09-20T14:32:04Z" },
{ "speaker": "customer", "text": "Yes, speaking.", "ts": "2026-09-20T14:32:07Z" }
],
"config": { "mode": "outbound_campaign", "script_text": "Hi Priya, this is Aria…" }
}

Live calls​

GET /api/voice/calls/live

Calls that have started and not yet ended:

{
"calls": [
{
"uuid": "cal-…", "call_type": "inbound",
"from_number": "+918888888888", "to_number": "+915550010000",
"started_at": "2026-09-20T14:32:01Z",
"customer_name": "Priya Kumar", "flow_name": "Support line"
}
]
}

Derived from the database rather than from in-memory state, so a call whose process died is still visible.

A call is listed here until its hangup webhook arrives. Ensure PUBLIC_BASE_URL is reachable by your telephony provider so that calls are closed out promptly.

Statistics​

GET /api/voice/calls/statistics?flow_uuid=…&date_from=2026-09-01

{
"total_calls": 891,
"connected_calls": 543,
"calls_today": 42,
"avg_duration": 97,
"connect_rate": 0.61
}

"Connected" means duration > 0. These figures cover the whole organization.

Export​

GET /api/voice/calls/{uuid}/export

Streams the filtered list as CSV, capped at 10,000 rows and ignoring pagination. It takes the same query parameters as the list endpoint; the {uuid} in the path is not used to select a single call.

Content-Type: text/csv
Content-Disposition: attachment; filename="calls.csv"

Outcomes​

ValueSet when
initiatedAn outbound call row is created, before dialling
completedThe provider reported a normal hangup
no_answerNobody picked up, or the dial timed out
busyBusy signal
failedDial failed, invalid or unallocated number
rejectedThe callee declined
cancelledThe call was cancelled before connecting

Human click-to-call adds agent_answered, agent_no_answer, customer_no_answer.

Determining whether a call connected​

outcome reflects what the telephony provider reported, and is most meaningful for calls that never connected — a no-answer, a busy signal, a failed dial.

For a call handled by the AI pipeline, use ended_at and duration instead:

SignalMeaning
duration > 0The call connected and audio was exchanged
ended_at is setThe call is over
ended_at is nullStill in progress

An outbound campaign call carries initiated from the moment its row is created, and an inbound AI call may report null, so neither value on its own distinguishes a completed conversation from one that never connected.

sentiment, summary, and key_details are present on the record and are populated by post-call analysis where that is configured.

Call endings​

The agent ends the call when its own reply contains a closing phrase — checked after the reply has been spoken, so the farewell is not cut off mid-word.

The phrases are matched as substrings, case-insensitively: have a good day, have a great day, thank you for calling, goodbye, take care, dhanyavaad, shubh din.

Matching is on the whole reply rather than only its final sentence, so a closing phrase used mid-conversation also ends the call.

Configure a greeting on every inbound flow

A configured greeting is spoken verbatim and does not pass through this check. Without one, the model composes its own opening, and an opening containing a closing phrase will end the call as soon as it is spoken. See Flows.

A separate set of phrases triggers a handoff to a human instead: connect you, put you through, transfer you, someone who can help, a colleague will, specialist will.

Barge-in​

The caller can talk over the agent. Speech detection cancels generation and flushes audio the provider has already buffered.

Interruption is detected while the agent's audio is being streamed to the provider. VOICE_VAD_MIN_VOLUME (default 0.045) sets the detection floor — lower it if callers report that talking over the agent has no effect, raise it if background noise is cutting the agent off.

Recordings​

Started over the provider's REST API once the media stream opens, and stopped explicitly when the call ends — an abandoned recording holds provider resources for the full hour and produces a file that is mostly silence.

recording_url is filled by the provider's recording callback, which can arrive well after everything else about the call was written. A transferred call keeps recording across the handover, so the file captures the human conversation too.