---
title: "Get Agent Session"
method: GET
path: "/v1/agent/sessions/{session_id}"
tags: ["Agent Sessions"]
---

# Get Agent Session

`GET /v1/agent/sessions/{session_id}`

## Request

- Base URL: `https://api.fish.audio`
- URL: `https://api.fish.audio/v1/agent/sessions/{session_id}`
- Auth: HTTP bearer

Fetch one session's full detail: status, timing, and the conversation
timeline (transcript turns interleaved with tool calls, in order).

## Path parameters

- `session_id` string, required

## Response `200`

Request fulfilled, document follows

- object
  - `session_id` string, required
  - `agent_id` string, required
  - `agent_name` string, required
  - `name` string, nullable
  - `end_user_id` string, nullable
  - `status` 'pending' | 'active' | 'completed' | 'failed' | 'unknown', required
  - `end_reason` 'user_hangup' | 'agent_hangup' | 'escalated' | 'conversation_timeout' | 'heartbeat_timeout' | 'agent_join_timeout' | 'internal_error' | 'llm_endpoint_failure' | 'room_join_timeout' | 'cancelled' | 'dial_failed', nullable
  - `source` 'api_key' | 'dashboard' | 'public' | 'phone' | 'whatsapp', required
  - `direction` 'inbound' | 'outbound'
  - `dial_status` 'answered' | 'busy' | 'no_answer' | 'failed', nullable
  - `answered_by` 'human' | 'voicemail' | 'unknown', nullable
  - `caller_number` string, nullable
  - `dialed_number` string, nullable
  - `sip_call_id` string, nullable
  - `call_sid` string, nullable
  - `timezone` string, nullable
  - `created_at` string, required
  - `started_at` string, nullable
  - `ended_at` string, nullable
  - `duration_seconds` integer, nullable
  - `participant_identity` string, nullable
  - `agent_participant_identity` string, nullable
  - `agent_joined_at` string, nullable
  - `metadata` object
  - `content_purged_at` string, nullable
  - `keypad_menu` AgentSessionKeypadMenu: How the keypad menu of an inbound phone call ended. `outcome` is `selected` when the caller chose an option, `no_input` or `no_match` when no attempt got a valid choice (the last one had no input, or input matching no option), and `skipped` when the menu could not run (a resumed conversation or no SIP caller). `value` is what the menu variable received: the chosen option's value, or the fallback value when the caller never made a valid choice.
    - `outcome` 'selected' | 'no_input' | 'no_match' | 'skipped', required
    - `variable` string, required
    - `value` string, required
    - `key` string, nullable
    - `input` 'dtmf' | 'speech', nullable
    - `attempts` integer
    - `completed_at` string, date-time, nullable
  - `unattributed_llm_usage` LLMMessageUsage[]
    - `message_generation_id` string, nullable
    - `turn_id` integer, nullable
    - `status` 'complete' | 'pending' | 'unavailable', required
    - `billing_mode` 'tokens' | 'included' | 'legacy', required
    - `call_count` integer
    - `unbilled_call_count` integer
    - `usage_complete` boolean
    - `call_ids` string[]
    - `cost_usd` string, nullable
    - `billable_cost_usd` string, nullable
    - `known_cost_usd` string
    - `tokens` LLMMessageTokenCost[]
      - `model` string, nullable, required
      - `rate_version` string, nullable, required
      - `category` 'input' | 'cache_read' | 'cache_write_5m' | 'cache_write_1h' | 'output', required
      - `tokens` integer, nullable, required
      - `rate_per_million` string, nullable, required
      - `cost_usd` string, nullable, required
  - `items` union[]
    - union
      - AgentSessionMessageItem: One transcript message; system messages never surface here.
        - `id` string, nullable
        - `runtime_message_id` string, nullable
        - `message_generation_id` string, nullable
        - `llm_usage` LLMMessageUsage
          - `message_generation_id` string, nullable
          - `turn_id` integer, nullable
          - `status` 'complete' | 'pending' | 'unavailable', required
          - `billing_mode` 'tokens' | 'included' | 'legacy', required
          - `call_count` integer
          - `unbilled_call_count` integer
          - `usage_complete` boolean
          - `call_ids` string[]
          - `cost_usd` string, nullable
          - `billable_cost_usd` string, nullable
          - `known_cost_usd` string
          - `tokens` LLMMessageTokenCost[]
            - `model` string, nullable, required
            - `rate_version` string, nullable, required
            - `category` 'input' | 'cache_read' | 'cache_write_5m' | 'cache_write_1h' | 'output', required
            - `tokens` integer, nullable, required
            - `rate_per_million` string, nullable, required
            - `cost_usd` string, nullable, required
        - `type` 'message'
        - `role` 'user' | 'assistant', required
        - `content` string, required
        - `input_medium` 'dtmf', nullable
        - `fallback_reason` 'llm_error', nullable
        - `answered_by_model` AgentSessionLlmModelRef: A model as the agent config names it: a platform model id in `model`, or a customer endpoint in `custom` (never its key).
          - `model` string, nullable
          - `custom` AgentSessionLlmCustomRef
            - `base_url` string, required
            - `model` string, required
        - `turn_id` integer, nullable
        - `created_at` string, nullable
      - AgentSessionToolCallItem: Tool invocation, mirroring the realtime `tool.started` event; `call_id` matches the SDK event's callId, so live UIs and history line up.
        - `type` 'tool_call'
        - `call_id` string, required
        - `tool_name` string, required
        - `tool_source` string, nullable
        - `input` string, required
        - `turn_id` integer, nullable
        - `created_at` string, nullable
      - AgentSessionTransferItem: One transfer attempt. `call_id` matches its `_transfer_call` tool_call item. A warm transfer's consult call (the caller on hold meanwhile) is in `messages`, never in the caller's message items. `merged` and `referred` succeeded, and `outcome` is null while the attempt runs.
        - `type` 'transfer'
        - `call_id` string, required
        - `destination` string, required
        - `destination_type` 'phone' | 'sip', required
        - `target` string, required
        - `mode` 'cold' | 'warm', required
        - `warm_connect` 'confirm' | 'briefing' | 'direct', nullable
        - `outcome` 'merged' | 'referred' | 'dial_failed' | 'declined' | 'voicemail' | 'ivr' | 'consult_closed' | 'consult_timeout' | 'merge_failed' | 'refer_failed' | 'caller_hangup' | 'cancelled' | 'unavailable', nullable
        - `sip_status` integer, nullable: dial_failed and refer_failed: the final SIP response code the carrier gave. Null when it gave none.
        - `dial_status` 'busy' | 'no_answer' | 'failed', nullable: dial_failed and refer_failed: why the transfer target could not be reached, classified from sip_status the same way as an outbound call's dial_status.
        - `decline_reason` string, nullable: declined: the agent's summary of why the transfer target declined. Null when none was given. What they actually said is in messages.
        - `consult_end_reason` 'transfer_target_hangup' | 'connection_lost', nullable: consult_closed: how the consult call ended before the caller was connected. Null when it cannot be told.
        - `hold_started_at` string, nullable
        - `hold_ended_at` string, nullable
        - `dial_started_at` string, nullable
        - `answered_at` string, nullable
        - `ended_at` string, nullable
        - `transferred_call_ended_at` string, nullable: merged and referred: when the call between the caller and the transfer target ended. Null while it is still on. A cold transfer waits for the carrier's call record (up to about an hour) and stays null on imported SIP numbers.
        - `transferred_call_duration_seconds` integer, nullable: merged and referred: how long the caller talked with the transfer target. Warm: from ended_at to transferred_call_ended_at. Cold: the carrier's call record, 0 when the target never really answered.
        - `transferred_call_end_reason` 'caller_hangup' | 'transfer_target_hangup' | 'conversation_timeout' | 'connection_lost', nullable: merged: how the call between the caller and the transfer target ended. caller_hangup: the caller hung up (on an outbound call, the person the agent called). transfer_target_hangup: the transfer target hung up. conversation_timeout: the call reached the agent's maximum call duration, counted from the start of the call. connection_lost: a line dropped, nobody hung up. Null when it cannot be told, and always null on a cold transfer.
        - `messages` AgentSessionTransferMessage[]
          - `role` 'assistant' | 'transfer_target', required
          - `content` string, required
          - `created_at` string, nullable
        - `created_at` string, nullable
      - AgentSessionToolResultItem: Tool outcome (`tool.completed`/`tool.failed` twin). A call that never resolved (session died mid-flight) has no result item.
        - `type` 'tool_result'
        - `call_id` string, required
        - `tool_name` string, required
        - `tool_source` string, nullable
        - `status` 'completed' | 'failed', required
        - `output` string, nullable
        - `output_truncated` boolean
        - `error` string, nullable
        - `rejected_by` 'parameter_validation', nullable
        - `latency_ms` integer, nullable
        - `turn_id` integer, nullable
        - `created_at` string, nullable
  - `analysis` AgentSessionAnalysisResult: Public trim of the post-call analysis record: outcomes only, no run internals (model, tokens, config hash).
    - `status` 'queued' | 'running' | 'completed' | 'skipped' | 'error', required
    - `summary` string, nullable
    - `data` AgentSessionAnalysisDataValue[]
      - `name` string, required
      - `type` 'boolean' | 'text' | 'number' | 'enum', required
      - `value` union
        - boolean
        - number
        - string
      - `rationale` string
    - `criteria_results` AgentSessionAnalysisCriterionResult[]
      - `name` string, required
      - `result` 'success' | 'failure' | 'unknown', required
      - `rationale` string
  - `llm_endpoint_errors` AgentSessionLlmEndpointError[]
    - `occurred_at` string, nullable
    - `turn_id` integer, nullable
    - `type` 'http_error' | 'timeout' | 'connection_error' | 'invalid_response', required
    - `reason` 'dns_failed' | 'connection_refused' | 'connection_reset' | 'tls_failed' | 'address_not_allowed' | 'not_event_stream' | 'stream_incomplete' | 'empty_reply' | 'unknown', nullable
    - `status_code` integer, nullable
    - `response_body` string, nullable
    - `duration_ms` integer, nullable
    - `consecutive_failures` integer, required
  - `llm_fallbacks` AgentSessionLlmFallback[]
    - `occurred_at` string, nullable
    - `turn_id` integer, nullable
    - `from` AgentSessionLlmModelRef, required: A model as the agent config names it: a platform model id in `model`, or a customer endpoint in `custom` (never its key).
      - `model` string, nullable
      - `custom` AgentSessionLlmCustomRef
        - `base_url` string, required
        - `model` string, required
    - `to` AgentSessionLlmModelRef, required: A model as the agent config names it: a platform model id in `model`, or a customer endpoint in `custom` (never its key).
      - `model` string, nullable
      - `custom` AgentSessionLlmCustomRef
        - `base_url` string, required
        - `model` string, required
    - `reason` 'error' | 'timeout' | 'ttft_timeout' | 'total_timeout' | 'empty' | 'kick_in', required

## Other responses

- `401`: No permission -- see authorization schemes
- `404`: Nothing matches the given URI
- `502`: Invalid responses from another server/proxy
- `503`: The server cannot process the request due to a high load

## Changes

- **2026-11-03** `42ac421f7a12`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/fish/apis/fishaudio-openapi/changes/v1/agent/sessions/:session_id/get.md)

---

[API](https://skmtc.dev/fish/apis/fishaudio-openapi.md) - [All operations](https://skmtc.dev/fish/apis/fishaudio-openapi/llms.txt) - [OpenAPI document](https://skmtc.dev/fish/apis/fishaudio-openapi/revisions/42ac421f7a12?raw)
