Skip to content

Calls & artifacts ​

A call is one conversation and everything it left behind: a transcript, a recording, a timeline of typed events, per-stage latency and a per-component cost breakdown.

API ​

Full parameters and responses: call reference.

GET    /api/v1/calls
POST   /api/v1/calls
GET    /api/v1/calls/search
GET    /api/v1/calls/{id}
DELETE /api/v1/calls/{id}
POST   /api/v1/calls/{id}/end
GET    /api/v1/calls/{id}/transcript
GET    /api/v1/calls/{id}/messages
GET    /api/v1/calls/{id}/events
GET    /api/v1/calls/{id}/metrics
GET    /api/v1/calls/{id}/costs
GET    /api/v1/calls/{id}/artifacts
GET    /api/v1/calls/{id}/debug
GET    /api/v1/calls/{id}/recording
DELETE /api/v1/calls/{id}/recording
GET    /api/v1/calls/{id}/artifacts/{artifact}/download   (signed URL, no API key)

GET /calls/search is the same handler as GET /calls

The two routes point at one controller method. Use whichever reads better; they behave identically.

The state machine ​

created → queued → ringing → connecting → connected
                                             │
        ┌────────────────────────────────────┴──────────────────────────┐
        │  listening ⇄ user_speaking ⇄ thinking ⇄ tool_executing        │
        │            ⇄ assistant_speaking ⇄ on_hold ⇄ transferring      │
        │            ⇄ human_queue ⇄ human_connected                    │
        └───────────────────────────────────────────────────────────────┘
                                             │
                                 ending → ended | failed

Every state inside the loop can follow every other, because the caller can interrupt at any instant — that is barge-in. ending, failed and ended are reachable from anywhere non-terminal. The table is defined once in packages/contracts/src/call-state.ts and mirrored in App\Enums\CallState, with a test that reads the PHP source to prove they agree. The 17 states are listed on Call states & events.

End reasons are the exception: the contracts have 19 and the control plane has 20, with different spellings, and no test holds them together. The API returns the control plane's twenty — they are listed on When something fails.

Events ​

Every call emits a typed event stream — lifecycle, speech, transcripts, model, TTS, tools, MCP, knowledge, routing, contact centre, DTMF, artifacts, provider health. The vocabulary is CallEventType in @callagent/contracts, mirrored in App\Enums\CallEventType. call_events is insert-only. All 70 event types and their payloads: event types.

Artifacts ​

ArtifactProduced when
TranscriptAlways, incrementally during the call
Recordingartifact_plan.recording_enabled. An announcement can be played; a refusal cannot be honoured — see AI disclosure & consent.
Summaryanalysis_plan after the call — not dispatched, see below
Structured outputAn assistant with a structured output attached — not dispatched, see below
Debug bundleOn demand, from GET /calls/{id}/debug

Recordings can be written to your own bucket — S3, R2, Azure Blob or any S3-compatible endpoint — under an organization-prefixed path, and are deletable per call for a GDPR erasure request. See Recording & storage.

Summary and structured output

The Summary and Structured output rows above depend on the post-call analysis pipeline, which the control plane does not currently dispatch. See Analytics.

Latency and cost ​

GET /calls/{id}/metrics breaks the turn down: first STT partial, endpoint detected, model time-to-first-token, TTS time-to-first-audio, and the end-to-end endpoint-to-audio figure that is what the caller actually experiences.

GET /calls/{id}/costs breaks the money down per component with the provider's own units, at decimal(20,8) — an LLM token costs less than a millionth of a dollar and rounding it early makes the ledger wrong.

Live calls ​

The dashboard's Live Calls view reads the gateway's session snapshots — state, current tool, last transcript, per-component latency, active providers.

Supervisors can listen, whisper or barge in from there. The permission gate and the audit trail are proven; that audio actually bridges is not, and the newer gateway-media path is off by default. See Contact centre.

Documented against the code. Anything not yet implemented is labelled, never described as working.