---
title: "Get Call Metadata"
method: GET
path: "/egress/calls/{call_id}"
tags: ["egress"]
---

# Get Call Metadata

`GET /egress/calls/{call_id}`

## Request

- Base URL: `https://dev.whispa.net` (the document declares no server; this is the origin it was published from)
- URL: `https://dev.whispa.net/egress/calls/{call_id}`
- Auth: none declared

Allowlisted metadata for a single call.

## Path parameters

- `call_id` string, uuid, required

## Response `200`

Successful Response

- EgressCallMetadata: Allowlisted call metadata (no customer identity, no audio).
  - `id` string, uuid, required
  - `scenario` string, nullable, required
  - `call_mode` 'training' | 'production' | 'ai_agent', required: Call mode enumeration.
  - `call_source` 'live' | 'uploaded', required: Call source enumeration.
  - `call_direction` 'incoming' | 'outgoing' | 'internal', required: Direction of the call. - INCOMING: Customer called agent - OUTGOING: Agent called customer - INTERNAL: Extension-to-extension call
  - `status` 'ongoing' | 'completed' | 'unanswered' | 'error', required: Call lifecycle state. - ONGOING: Call is currently in progress - COMPLETED: Call finished (regardless of whether it was answered) - ERROR: Technical error/exception during call processing - UNANSWERED: DEPRECATED - Use status=COMPLETED + telephony_result=NO_ANSWER instead. Kept for backwards compatibility with existing database records.
  - `started_at` string, date-time, required
  - `ended_at` string, date-time, nullable, required
  - `duration_seconds` integer, nullable, required
  - `conversation_outcome` 'engaged' | 'refused' | 'wrong_person' | 'other', required: AI-classified judgement of what kind of conversation occurred. Only applicable when answered_by=HUMAN. Runtime actions such as scheduling a callback or transferring the caller are recorded independently in ``WorkflowDisposition`` and must never overwrite this judgement.
  - `workflow_disposition` 'none' | 'callback_scheduled' | 'transferred' | 'human_followup_required' | 'do_not_contact', required: Deterministic action the autonomous workflow took before finalizing. ``NONE`` is explicit for finalized autonomous calls with no special handoff/follow-up action. NULL means the dimension does not apply (human copilot, uploaded, or legacy calls), rather than overloading NULL with two meanings.
  - `agent_end_reason` 'conversation_complete' | 'existing_arrangement_claimed' | 'customer_requested_human' | 'identity_verification_failed' | 'customer_declined_to_engage' | 'customer_unavailable' | 'wrong_person' | 'do_not_contact' | 'hardship_detected' | 'dispute_raised' | 'escalation' | 'voicemail' | 'no_answer' | 'caller_hung_up' | 'caller_abandoned' | 'answer_failed' | 'answer_timeout' | 'dial_failed' | 'transfer_failed' | 'screening_timeout' | 'idle_timeout' | 'max_duration' | 'api_hangup' | 'error' | 'other', required: Normalized reason an autonomous call ended. The raw runtime string remains in ``analyzer_artifacts`` for diagnostics. This enum is the stable operational projection: semantic reasons exposed to the voice model plus bounded technical termination categories. Novel runtime endings degrade to ``OTHER`` instead of failing finalization.
  - `answered_by` 'human' | 'voicemail' | 'unclear', required: Who answered the call (AI-classified from transcript). This is the first stage of call classification - determining whether a human or voicemail answered the call.
  - `telephony_result` 'connected' | 'busy' | 'no_answer' | 'rejected' | 'failed', required: Telephony-level call connection result. Represents what happened at the telephony level before any conversation. This is typically provided by the PBX/telephony system, not AI-classified.
  - `detected_scenario` string, nullable, required
  - `client` string, nullable, required
  - `qa_score` integer, nullable, required
  - `idv_outcome` 'verified' | 'partial' | 'incomplete', required: Identity-verification outcome for a call, denormalized from the IDV analyzer. Only set when IDV was required and a verdict was produced. A NULL column can mean IDV was not applicable, is still pending, or no verdict was available; API consumers use ``idv_required`` plus call status to distinguish those states. Derived from the terminal `identity_verification_state["status"]`: - VERIFIED: status `complete` — identity confirmed per the scenario's policy. - PARTIAL: at least one identity factor was confirmed, but the scenario's completion rule was never satisfied. - INCOMPLETE: required, and not a single factor was confirmed. PARTIAL exists because "not verified" covers two operationally different calls: one where the customer engaged with verification and it ran out of road, and one where it never started. Only the second is a candidate for "the agent didn't try". Note this is *tracking* state, not an adherence grade — the leadership IDV rate deliberately reads the reviewed QA scorecard instead (see `shared.idv`), because a human reviewer can correct it and the analyzer cannot. There is deliberately no `skipped` outcome. The live state machine still has a `skipped` terminal so the analyzer stops firing on a call that is not going to verify, but as a *result* it said nothing true that `incomplete` doesn't: verification was required and did not happen. It also read as a third, ambiguous colour on any report — neither pass nor fail — while the thing a reviewer actually needs to know (was skipping appropriate here?) is a transcript judgement the tracker never made.
  - `idv_source` 'live' | 'post_call' | 'inherited', required: Which mechanism produced the call's persisted IDV state. - LIVE: the in-call IDV analyzer's terminal state (the normal path). - POST_CALL: the post-call IDV judge — runs when the live analyzer never produced a state (e.g. no scenario reached the session) or the scenario was reclassified after the call, judging the full transcript against the resolved scenario's IDV rules. - INHERITED: the origin leg of a transfer verified the customer, and this recipient leg inherits that verdict — verification travels with the conversation, not the leg (see calls.idv_resolution). The verification exchange is on the origin leg's transcript, not this one's.
  - `recording_disclosed` boolean, nullable, required
  - `tags` unknown
  - `agent_id` string, nullable, required
  - `account_id` string, uuid, nullable: Opaque Whispa account row id (the Accounts page id) the call is linked to; null when unlinked or the account was never synced

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/whispa/apis/whispa-api.md) - [All operations](https://skmtc.dev/whispa/apis/whispa-api/llms.txt) - [OpenAPI document](https://skmtc.dev/whispa/apis/whispa-api/revisions/12c2e4aca43c?raw)
