---
title: "List AI Assistant chat messages"
method: GET
path: "/assistant/chat/conversations/{conversation_id}/messages/"
tags: ["assistant"]
---

# List AI Assistant chat messages

`GET /assistant/chat/conversations/{conversation_id}/messages/`

## Request

- Base URL: `https://api.getoneprofile.ai` (the document declares no server; this is the origin it was published from)
- URL: `https://api.getoneprofile.ai/assistant/chat/conversations/{conversation_id}/messages/`
- Auth: one of:
  - API key in header `X-API-Key`
  - HTTP bearer

List a conversation's messages in the order they happened, each reply with the trail it showed and the action cards it raised, as they stand now. A reply whose `status` is `pending` is a turn still running: its content is the words written so far, up to a second behind, and its trail the steps taken so far. A turn whose process died before that is shown `failed` once its time is up.

## Path parameters

- `conversation_id` string, uuid, required

## Response `200`

Successful Response

- WorkspaceChatMessageListResponseSchema: A conversation's transcript, oldest first.
  - `messages` WorkspaceChatMessageReadSchema[], required
    - `actions` ActionProposalReadSchema[], required: The actions this reply proposed or applied, in the order the AI Assistant named them, as they stand now. Empty for user messages and replies that acted on nothing.
      - `applied_result` object, nullable, required: The verb's or tool's own record of what it made; null until applied. A tool card carries `content` (what the assistant read back), `link` (an app path to the thing), `link_label` (its name), `entity_id`, `opens` (whether the page should go to the link when this lands) and `made` (what was created, for the AI-made mark), plus any facts the tool added.
      - `approve_label` string, required: What the Approve button says: "Approve", or with the most it spends, "Approve · up to 8 credits", or with a flat price, "Approve · 8 credits", or per day, "Approve · up to 145 credits a day".
      - `batch_id` string, uuid, nullable, required: Cards the assistant raised together in one reply share this, so they can be shown and approved as a group. Null for a card raised on its own.
      - `charged_credits` number, nullable: What the run this card started was charged, in credits, once it finished. Null until then, and for every card that starts no run.
      - `conversation_id` string, uuid, nullable, required
      - `decided_at` string, date-time, nullable, required
      - `decided_by_display_name` string, nullable, required
      - `destructive` boolean: Whether approving this removes something. A destructive card is drawn apart from the safe ones, is never offered a standing grant, and is listed on its own in the confirm an Approve all opens.
      - `detail` ProposalDetailSchema: The body of a card: what to read under the summary before approving. ``kind`` says how to draw it. ``text`` is a block to read; ``diff`` is a before and an after to compare, which is how a prompt or an email being rewritten is shown, so nobody approves a rewrite they have not seen. A card about a lead search also carries the search: under a ``text`` card's text, or as the first lines of a ``diff``'s before and after. The text then holds only what is not a filter.
        - `after` string: What it would say, for a diff.
        - `before` string: What it says now, for a diff.
        - `kind` 'text' | 'diff', required
        - `landing` CardLandingSchema: The table a lead search's card would make: its name, how many it adds, a few who match. The card leads with it, so a person sees the list they asked for before approving it. Every word is the server's; the dashboard draws it as given.
          - `adds` string, required: How many rows it adds out of how many match: "Adds 10 of 608 people".
          - `sample_columns` string[]: The sample's column names, as the table names them.
          - `sample_rows` array[]: A few who match, one value per column. Empty for a search whose rows are bought.
            - string[]
          - `table_name` string, required: The new table's name, the card's title.
        - `search` CardSearchSchema: A lead search a card lists the filters of, as the search route takes it. Drawn by the dashboard through the Find leads page's own summary, so a card names each filter as the page does, never by its route name.
          - `query` object: The search's filters, filter name to value.
          - `record_type` 'people' | 'companies' | 'signal_companies' | 'signal_people' | 'lookalikes' | 'local_businesses' | 'hiring_companies' | 'backlinks' | 'search_results' | 'post_authors', required: Which dataset a search runs against, and so which provider serves it. Values are persisted in String(16) columns, so every member must stay within 16 characters.
        - `search_before` CardSearchSchema: A lead search a card lists the filters of, as the search route takes it. Drawn by the dashboard through the Find leads page's own summary, so a card names each filter as the page does, never by its route name.
          - `query` object: The search's filters, filter name to value.
          - `record_type` 'people' | 'companies' | 'signal_companies' | 'signal_people' | 'lookalikes' | 'local_businesses' | 'hiring_companies' | 'backlinks' | 'search_results' | 'post_authors', required: Which dataset a search runs against, and so which provider serves it. Values are persisted in String(16) columns, so every member must stay within 16 characters.
        - `text` string: The body, for a text card.
      - `error` string, nullable, required: Why an approval failed to apply, in plain words; null otherwise.
      - `expected_credits` number, nullable: What the run usually costs, in credits, read from what the same kind of cell has been charged before; equal to price_credits for a flat price, which the card then says as one figure. Null for a card whose price is the whole story.
      - `grantable` boolean, required: Whether the card may offer Approve and allow in this conversation: only a pending proposal of a verb that is not a send.
      - `id` string, uuid, required
      - `kind` 'verb' | 'tool', required: How an approved card is carried out. VERB is F-15's eight hand-written workspace verbs: each has a spec in the registry with its own apply, because applying one is real work rather than a single request. TOOL is any route the assistant may call. An approval re-issues the request the model asked for, through the same executor an MCP client would use, so there is nothing to write per route and nothing to keep in step.
      - `loss` string, nullable: What approving a destructive card takes away, and whether it comes back, in one sentence; null when nothing is removed.
      - `message_id` string, uuid, nullable, required
      - `origin` 'chat' | 'agent' | 'scheduled_search' | 'table_assistant', required: Which surface raised a proposal. Kept as its own enum rather than reusing the thread-store origins: a scheduled search proposes with no conversation behind it at all, and the activity feed needs to say so.
      - `prepared_column` PreparedColumnSchema: The column a card created before anybody decided it. Adding a column and running it are one decision: the column lands the moment the card appears, so the person can open it and see what they are being asked to pay for, and the card quotes the run alone. Dismissing the card removes the column again.
        - `column_id` string, uuid, nullable, required: The column the card runs, when the card added it. Null when the card runs a column that was already on the table and added only columns that read it.
        - `dependent_column_ids` string[]: Columns the same reply added that the run fills in after this one. Dismissing the card removes them too. Empty for most cards.
        - `table_id` string, uuid, required
      - `price_aside` string, nullable, required: What a waiting card says beside its Approve button, when the button does not say it all: "usually about 24", "Free", "Free to build · sending about 45 credits". Null when the button says everything.
      - `price_credits` number, nullable, required: The published price of the operation when it was proposed, in credits; null when free.
      - `price_label` string, required: What the card says it costs: "Free", or the most it can spend, "Up to 16.5 credits"; for a run charged by use, "Up to 65 credits · usually about 24"; for a flat price (a run of enrichments that each have one price), the one figure, "2,800 credits"; once that run has ended, "Charged 23.8 credits"; and for a card priced per day, "Up to 145 credits a day", before and after it is approved.
      - `price_per` 'call' | 'day': What a card's price is the price of: one call, or each day of what the call starts. CALL is nearly every card: the most approving it may spend, once. DAY is a card that starts (or re-arms) a standing buy that goes on spending by itself, adding rows every day: its price is the most one day may cost, said "up to 145 credits a day", and Approve all keeps it out of the one-off total it adds up. No tool starts one today.
      - `proposed_at` string, date-time, required
      - `runs_column_id` string, uuid, nullable: The column approving this card runs, while the card waits: the one it added, or one already on the table that it names. Lets the column's own panel approve this card instead of starting a second run. Null for a card that runs no column, and once the card is decided.
      - `source_column_id` string, uuid, nullable, required: The agent column whose run raised the card; null for every other origin. What puts a card on its table's page when its arguments name a sequence or a destination instead of the table.
      - `source_record_id` string, uuid, nullable, required: The row the agent was working on when it raised the card; null otherwise.
      - `status` 'proposed' | 'applied' | 'undone' | 'dismissed' | 'failed', required: Where one proposed action stands. PROPOSED waits on a person. APPLIED happened. DISMISSED is a person saying no. FAILED is an approval whose apply raised; the error stays with the row so the card can say why instead of vanishing. UNDONE is HISTORY ONLY. Nothing reaches it any more: undo was taken out while the assistant moved from eight hand-written verbs to any route it may call, because reversing an arbitrary write is per-route work nobody has done yet. The member stays because rows in this state were written before that, and a status the code cannot read is an activity feed that raises instead of loading.
      - `summary` string, required
      - `verb` string, required: What the card proposes: one of the eight workspace verbs, or the name of an API operation the assistant asked to call. `kind` says which.
      - `verb_label` string, required
    - `attachments` ChatAttachmentReadSchema[], required: The files sent with this message, in the order they were added, each as the card above the message draws it. Empty for a reply, and for a message sent with none.
      - `column_count` integer, required
      - `columns` string[], required: The header row's names, in order; empty for a PDF.
      - `created_at` string, date-time, required
      - `filename` string, required
      - `id` string, uuid, required
      - `kind` 'spreadsheet' | 'pdf', required: What kind of thing a file the person attached is, for the card that shows it. Each kind is read by its own reader and drawn under its own icon: a CSV or an Excel file is a spreadsheet, rows under a header; a PDF is pages of text, and the rows of the table its pages hold when one is asked for.
      - `page_count` integer, nullable, required: A PDF's pages; null for a spreadsheet.
      - `row_count` integer, required: Data rows under the header row; 0 for a PDF, whose table is read into rows only when its rows are first read.
      - `sent` boolean, required: Whether the file has been sent with a message. A file added and never sent can be removed; one already sent stays with its message.
      - `sheet` string, nullable, required: The workbook sheet the rows were read from; null for a CSV or a PDF.
      - `size_bytes` integer, required
    - `content` string, required
    - `conversation_id` string, uuid, required
    - `created_at` string, date-time, required
    - `duration_ms` integer, nullable, required: How long this turn took end to end, in milliseconds. Null for your own messages and for a reply saved before the clock existed.
    - `id` string, uuid, required
    - `page` PageContextSchema, required: What the person had open when they sent the message. So "this table" resolves without them naming it. Sent by the dashboard, optional everywhere: a message from anywhere else simply carries none. Kept with the message and read back with it, so the transcript can say where each message was sent from.
      - `entity_id` string, nullable: The id of that thing.
      - `entity_name` string, nullable: What it is called, as the page showed it, so the assistant can say it back. Cut to 255 characters rather than refused.
      - `entity_type` string, nullable: What the page is about: `table`, `sequence`, `agent`, `integration`, `inbox thread`, `sender` or `mailbox`. Null on a page about no one thing.
      - `page_name` string, nullable: The page or tab they have open, in the screen's own words: a sequence's "Audience" tab, an agent's "Train", or a page about no one thing, such as "Tasks".
      - `route` string, nullable: The path they have open, e.g. `/tables/1234`.
      - `selected_step_id` string, nullable: The selected sequence builder step; resolve it against the sequence before editing.
      - `selected_step_label` string, nullable: The selected step as the Builder numbers and names it, e.g. "Step 1 · Opener". The id is what to edit by; this is how the person sees it.
    - `pasted_count` integer, required: How many entries the person pasted alongside these words. The list itself is not returned: the transcript shows "128 domains", not the domains. Zero when nothing was pasted.
    - `pasted_kind` 'domains' | 'emails' | 'names' | 'entries', required: What a pasted list turned out to hold, so the transcript can say "128 domains". The composer works this out from the entries before sending; the server works it out again from the stored entries when the transcript is read, with the same rule, so the card a person saw before pressing Enter is the card the conversation keeps. ``ENTRIES`` is a list of nothing in particular, which still counts.
    - `plan` string[], required: The task list the AI Assistant published before starting a job with several steps in it, in order. Empty when it did not publish one, which is every short answer.
    - `plan_done` integer, required: How many of the plan's steps the AI Assistant marked done, counted from the first: 2 means the first two are done. Zero when nothing was marked, and for a reply with no plan.
    - `question` WorkspaceChatQuestion, required: One clarifying question a turn ended on, with the answers to tap. ``options`` are whole replies, ready to send as the next message, never letters or numbers to pick from: tapping one is the same as typing it, so a person who wants to say something else still can.
      - `options` string[]
      - `text` string, required
    - `quote` string, nullable, required: The part of an earlier reply the person selected and asked about, as it stood when they took it, shown above their words. Null for a reply, and for a message that quoted nothing.
    - `role` 'user' | 'assistant', required: Who wrote a message.
    - `segments` WorkspaceChatSegmentReadSchema[], required: A reply that paused for approval, split where it paused: each part's text, then the cards that part raised, in order. Draw each part's text directly above its own cards. `content` still holds every part's text joined, and a `failed` reply's `content` is its failure sentence, which is in no part. Empty for a reply that never paused and for replies saved before parts were kept: draw `content`, then every card.
      - `action_ids` string[], required: The ids of the cards this part raised, in order, to draw under its text. Each is one of the reply's `actions`; an id with no card there is a card gone since, and is skipped.
      - `text` string, required: What this part of the reply said, as markdown. Empty when the part only raised cards.
    - `sequence` integer, required
    - `status` 'pending' | 'waiting' | 'complete' | 'failed', required: Whether a turn finished. A reply is written PENDING in the same transaction as the person's message, before the model is called, and filled in as COMPLETE or FAILED once the turn is saved. So a question is never on disk without a reply row under it: a crashed turn is saved FAILED rather than dropped, and a turn whose process died between the two transactions is a reply still pending, which the abandoned-turn sweep fails. Either way the user sees a reply they can retry instead of a question with nothing under it.
    - `steps` AiStep[], required: The trail this turn showed while it ran, as it was shown. Empty for user messages.
      - `action` string, required: What kind of move this was, e.g. `read_table`, `plan`, `write_column`.
      - `detail` string: The step's input: the instruction, the column it read, the query it ran.
      - `duration_ms` integer, nullable: Wall clock for the work this step describes, never an estimate.
      - `observation` string: One line on what came back. The half that makes a step worth reading.
      - `outcome` string: `completed`, `failed`, `refused` or `stopped`. Clients must tolerate new tokens.
      - `thought` string, nullable: The model's stated reasoning for choosing this step. Set by the agent loop; the assistant surfaces leave it null, and the renderer simply omits the rung.
    - `suggestion_kinds` NextStepKind[], required: What each of `suggestions` is about, in the same order and as many, which picks the icon it wears: `business_context`, `email`, `phone`, `sequence`, `agent`, `table`, `search`, `inbox`, `mailbox` or `other`. Empty for a reply saved before kinds were kept.
    - `suggestions` string[], required: Up to three follow-ups the AI Assistant offered, each written as a message you would send. Empty when it offered none.
  - `total_count` integer, required

## Other responses

- `401`: Unauthorized
- `403`: Forbidden
- `404`: Conversation not found
- `422`: Invalid request made to the API.
- `500`: Internal server error.
- `502`: Bad gateway error.
- `503`: Service unavailable.

---

[API](https://skmtc.dev/getoneprofile/apis/api-reference-oneprofile.md) · [All operations](https://skmtc.dev/getoneprofile/apis/api-reference-oneprofile/llms.txt) · [OpenAPI document](https://skmtc.dev/getoneprofile/apis/api-reference-oneprofile/revisions/4a6bd71e3f02?raw)
