---
title: "List Calls"
method: GET
path: "/egress/calls"
tags: ["egress"]
---

# List Calls

`GET /egress/calls`

## 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`
- Auth: none declared

Completed calls, newest first by (started_at, id), optionally human-only.

Keep all filters unchanged when following next_cursor. Save checkpoint
after storing the selected backfill to discover subsequent changes.
This is a live listing, not a frozen historical snapshot.

## Query parameters

- `answered_by` 'human' | 'voicemail' | 'unclear': 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.
- `call_mode` 'training' | 'production' | 'ai_agent': Call mode enumeration.
- `call_direction` 'incoming' | 'outgoing' | 'internal': Direction of the call. - INCOMING: Customer called agent - OUTGOING: Agent called customer - INTERNAL: Extension-to-extension call
- `account_id` string, uuid, nullable: Only calls linked to this Whispa account
- `started_after` string, date-time, nullable: Only calls that started at or after this instant
- `limit` integer
- `cursor` string, nullable

## Response `200`

Successful Response

- EgressDiscoveryPage
  - `items` EgressDiscoveredCall[], required
    - `metadata` EgressCallMetadata, required: 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
    - `revision` string, required: Opaque persisted revision; deduplicate with call ID
    - `changed_at` string, date-time, required
  - `next_cursor` string, nullable, required: Continue this page sequence, if present
  - `checkpoint` string, nullable, required: Resume /egress/calls/changes after durably saving the results. Change feeds issue a checkpoint only after the window is drained.

## 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)
