Skip to main content

Calls

An analysed recording. Everything is scoped to the active project — there is no project parameter on these routes.

List calls​

GET /api/calls

ParamNotes
outcomeresolved, follow_up_scheduled, visit_scheduled, escalated, interested, no_interest, voicemail, failed
sentimentpositive, neutral, negative
directioninbound or outbound
source_typeHow it arrived — e.g. manual_upload, public_api
customer_idRestrict to one customer
segment_uuidsComma-separated, OR-matched
searchFree text
unassigned1 returns only calls with no segment
limit / offset

An unknown segment_uuid is a 400, not a silently empty page.

Get a call​

GET /api/calls/{id}

{
"id": 1042,
"uuid": "sc1a2b3c-4d5e-6f70-8192-a3b4c5d6e7f8",
"customer_id": 87,
"customer_name": "Priya Kumar",
"direction": "outbound",
"source_type": "manual_upload",
"segment": "winback",
"segment_name": "winback",
"agent_name": "Arjun Rao",
"agent_type": "human",
"duration_seconds": 113,
"status": "completed",
"error_message": null,
"outcome": "follow_up_scheduled",
"sentiment": "positive",
"summary": "Customer interested in the Standing Desk Pro; asked about delivery.",
"key_details": [
{ "label": "Promised date", "value": "2026-09-27" }
],
"custom_field_values": { "budget_confirmed": true, "decision_maker": "Priya Kumar" },
"scores": { "opening": 3.0, "probing": 2.0, "objection_handling": 3.0 },
"goal_status": "achieved",
"goal_reasoning": "Follow-up call booked for the 27th.",
"recording_url": "/api/calls/1042/recording",
"transcript": [
{ "speaker": "agent", "text": "Hi, is that Priya?", "start": 0.0 },
{ "speaker": "customer", "text": "Yes, speaking.", "start": 2.4 }
],
"polished_transcript": [],
"polish_status": null,
"segments": [{ "uuid": "sg1a…", "name": "winback" }],
"is_flagged": false,
"processing_skip_reason": null,
"coaching_analysis": null,
"speech_duration_seconds": 96.2,
"silence_duration_seconds": 16.8,
"waveform_peaks": [0.02, 0.31, 0.44],
"started_at": "2026-09-20T14:32:01Z",
"ended_at": "2026-09-20T14:33:54Z"
}
Field groupWhat it is
outcome, sentiment, summaryModel-extracted conclusions
key_detailsLabelled facts pulled from the conversation
custom_field_valuesYour configured extraction fields
scoresPer-criterion rubric scores
goal_status, goal_reasoningWhether the call achieved its purpose, and why
speech_*, silence_*, waveform_peaksMeasured before transcription; null on older rows
is_flaggedFlagged for review
processing_skip_reasonWhy the pipeline skipped this recording, when it did
coaching_analysisCoaching notes, where that analysis is configured
segment / segment_nameThe single segment string carried on the call row, distinct from the many-to-many segments array

Statuses​

StatusMeaning
pendingQueued
awaiting_recordingRow exists, audio has not arrived
transcribingSpeech recognition running
completedAnalysed
failedPipeline failure — see error_message

Update a call​

PATCH /api/calls/{id}

{
"customer_name": "Priya Kumar",
"customer_phone": "+918888888888",
"customer_segment": "winback",
"customer_id": 87,
"new_customer": false,
"is_flagged": true
}
FieldTypeNotes
customer_namestring
customer_phonestring
customer_segmentstring
customer_idintegerReattach the call to an existing customer
new_customerboolCreate a new customer from the name and phone above instead of matching
is_flaggedboolFlag or unflag for review

Every field is optional; send only what changes. Model output such as scores, outcome, and summary is produced by analysis and cannot be set here, and neither can direction — that is fixed at upload.

Delete a call​

DELETE /api/calls/{id} → 204

Retry a call​

POST /api/calls/{id}/retry

Re-queues a failed call. The stored audio is reused, so there is nothing to re-upload. The call returns to pending.

Use this after fixing whatever caused the failure — a missing model route, an expired provider key, ffmpeg not on PATH.

Re-analyze a call​

POST /api/calls/{id}/reanalyze — superadmin only

Runs the analysis again on a call that already succeeded, picking up changed extraction fields or prompts. Restricted because re-running analysis across a deployment is expensive.

Polish a transcript​

POST /api/calls/{id}/polish-transcript

Produces a cleaned-up transcript — punctuation, speaker attribution, filler removal — into polished_transcript, leaving the raw one intact. polish_status tracks progress.

The original is never overwritten: polishing is a model pass and can be wrong, and a disputed call needs the unedited version.

Recording playback​

GET /api/calls/{id}/recording?token=<session-token>

Streams the audio. This route accepts the token in the Authorization header or as a ?token= query parameter, because a plain <audio src> cannot set a header.

Segments on a call​

PUT /api/calls/{id}/segments

{ "segment_uuids": ["sg1a2b3c-…", "sg4d5e6f-…"] }

Replaces the call's segments. To add across many calls at once, use POST /api/calls/bulk-segments — see Segments.

Flagged and skipped calls​

A call can carry a reason explaining why it was skipped by the pipeline, or why one that does have a transcript was flagged — a recording that is almost all silence, or too short to analyse. Those are surfaced rather than quietly producing an empty analysis that looks like a real one.

Filter with unassigned=1 to find calls that were never sorted into a segment.