---
title: "Read a conversation"
method: GET
path: "/api/v1/conversations/{thread_id}"
tags: ["authed"]
---

# Read a conversation

`GET /api/v1/conversations/{thread_id}`

## Request

- Base URL: `https://api.teaflask.com` (the document declares no server; this is the origin it was published from)
- URL: `https://api.teaflask.com/api/v1/conversations/{thread_id}`
- Auth: none declared

One conversation's exchanges with their status, cost and token
counts. Returns the newest exchanges, oldest first; page backwards for
earlier ones. It holds what visitors wrote but not the assistant's
replies: read those with get_conversation_transcript. A full page means
earlier exchanges may exist; turn_count always counts the whole
conversation.

## Path parameters

- `thread_id` string, uuid, required: The conversation's id, as list_conversations or search_conversations returns it.

## Query parameters

- `before_created_at` string, date-time, nullable: The created_at of the oldest exchange already read; pass with before_id to read earlier ones.
- `before_id` string, uuid, nullable: The id of the oldest exchange already read; pass with before_created_at.
- `limit` integer: The most exchanges to return.

## Response `200`

Successful Response

- ConversationDetailResponse: A conversation and a window of its exchanges.
  - `conversation` ConversationResponse, required: One conversation between the organization's assistant and a visitor to the organization's product.
    - `id` string, uuid, required: The conversation's id.
    - `title` string, required: A short title: the first line of the first message, or its first file's name when it had no text.
    - `owner_end_user_id` string, nullable, required: The organization's own id for the identified visitor; null for an anonymous visitor.
    - `channel` 'web' | 'slack', required: Where a conversation happened.
    - `owner_display_name` string, nullable, required: Who the conversation belongs to where it happened: the person's Slack name for a direct message, or #channel for a Slack channel; null for website visitors.
    - `agent_definition_id` string, uuid, nullable, required: The agent that answered the latest exchange that recorded one; null when none did or the organization's default assistant answered.
    - `agent_name` string, nullable, required: That agent's name; null when the agent is unknown.
    - `last_turn_status` 'queued' | 'working' | 'awaiting_input' | 'succeeded' | 'failed' | 'parked' | 'superseded' | 'stopped', required
    - `turn_count` integer, required: How many exchanges the whole conversation holds.
    - `total_charged_microusd` integer, required: What the conversation cost, in millionths of a US dollar, including work done outside any single exchange.
    - `created_at` string, date-time, required: When the conversation started.
    - `updated_at` string, date-time, required: When the conversation was last active.
  - `stream_thread_id` string, required: The conversation's id in its event stream.
  - `turns` ConversationTurnResponse[], required: The window of exchanges, oldest first.
    - `id` string, uuid, required: The exchange's id.
    - `user_message` string, required: What the visitor wrote, exactly as sent (for Slack, without the name and earlier thread messages the assistant was given with it); empty for an exchange the assistant started itself or a message of files alone.
    - `speaker_display_name` string, nullable, required: Who wrote this message, for a Slack conversation; null otherwise.
    - `kind` 'delivery', required
    - `attachments` TurnAttachmentResponse[]: The files the message carried, in upload order.
      - `id` string, uuid, required: The attachment's id.
      - `kind` 'image' | 'document' | 'video' | 'audio' | 'file', required: The assistant_attachments kind CHECK's closed set.
      - `format` string, nullable, required: The file's format, such as png or pdf; null when unknown.
      - `filename` string, required: The file's name as uploaded.
      - `byte_size` integer, required: The file's size in bytes.
    - `status` 'queued' | 'working' | 'awaiting_input' | 'succeeded' | 'failed' | 'parked' | 'superseded' | 'stopped', required
    - `error` string, nullable, required: What stopped the exchange, in plain words; set only when it failed.
    - `failure_reason` TurnFailureReason: Why the exchange failed: what kind of failure it was, and its facts.
      - `kind` 'no_backup_outage' | 'own_key_throttled' | 'own_key_outage' | 'own_key_declined' | 'own_key_required' | 'own_key_verifying' | 'backup_too_small' | 'summary_unavailable' | 'unfunded' | 'pocket_change_cap' | 'empty_balance' | 'model_contract_fault' | 'model_content_refused' | 'model_context_overflow' | 'model_provider_throttled' | 'subscription_unfunded' | 'subscription_unservable' | 'subscription_connect_required' | 'not_configured' | 'worker_lost' | 'runaway' | 'own_key_out_of_credit' | 'own_key_limited' | 'model_unlisted' | 'open_models_unavailable' | 'open_models_switched_off' | 'own_account_blocks_model', required: What kind of failure ended the exchange.
      - `detail` TurnFailureDetail: The facts behind the failure; each kind carries only its own.
        - `model_id` string, nullable: The model that was answering.
        - `provider` 'anthropic' | 'openai' | 'openrouter': Whose API a model call dials — the dimension BYOK keys, probes, bounce classification, and platform-key selection all key on. The vocabulary is append-only and each new value arrives with its integration (probe + classifier + key-shape rule + a platform key unless BYOK-only), never ahead of it.
        - `funding_source` 'balance' | 'byok' | 'end_user_subscription': Which pocket a model call bills against: the org's prepaid balance, the org's BYOK key, or an identified visitor's connected subscription. Per call, not per turn — one turn's rows may mix pockets. Closed partition under the llm_call_usage CHECK; the deduction paths drain the balance only for BALANCE rows. Complimentary money is BALANCE spend.
        - `which` 'message' | 'conversation', nullable: For a request too long for the model: whether the message alone or the whole conversation was too long.
        - `cause` 'tokens' | 'bytes' | 'summary_rejected', nullable: For a request too long for the model: what ran out.
        - `window_tokens` integer, nullable: How many tokens the model can read at once.
        - `plan_lapse` 'quota' | 'auth': Why a connected plan stopped paying: its usage limit, or its sign-in.
        - `balance_empty` boolean, nullable: For a declined API key: true when the balance was empty too, so neither could pay.
        - `welcome_credit_locked` boolean, nullable: For an empty balance: true when the organization had never added a card, so its welcome credit was still locked.
        - `redo_attempted` boolean, nullable: For an assistant that stopped unexpectedly: true when the step was already started again once on a fresh machine before it was given up.
        - `writer_prefix` string, nullable: For an assistant that stopped unexpectedly: the lost step's run and step ids, the prefix every write it made is labelled with — for support to find its trail.
        - `resets_at` string, date-time, nullable: When the key's spending limit resets.
    - `run_id` string, nullable, required: The exchange's id in the conversation's event stream; null when it was never recorded.
    - `charged_microusd` integer, required: What this exchange cost, in millionths of a US dollar.
    - `input_tokens` integer, required: Uncached prompt tokens the exchange's model calls read.
    - `output_tokens` integer, required: Tokens the exchange's model calls wrote.
    - `cache_read_tokens` integer, required: Prompt tokens served from the prompt cache.
    - `cache_write_5m_tokens` integer, required: Prompt tokens written to the five-minute prompt cache.
    - `partial_calls` integer: How many model calls were cut short without reporting their tokens; when above zero, the token counts are lower bounds.
    - `created_at` string, date-time, required: When the exchange started.

## Other responses

- `401`: Unauthorized
- `403`: Forbidden
- `404`: Not found
- `405`: Method not allowed
- `422`: Validation error
- `500`: Internal server error

---

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