:::endpoint GET /v1/admin/tickets/{ticket}/messages

The tab Conversation of the website's Tickets screen: the trader, the team,
the assistant, the lines the system adds, and the team's internal notes
between them, in the order they were written. The whole thread comes at once,
without pages, because a conversation is one thing to read.

Reading it marks nothing read, as on the website. Tell the server the team has
seen it with [mark read](/docs/api/admin/ticket-read). New messages arrive on
the ticket's `channel` while it is open, see
[listen on a websocket](/docs/api/apps/realtime#the-ticket-channels).

Needs a firm. Our own staff name one with the `Propper-Workspace` header.

## Request

```bash
curl "https://api.proppertrading.com/v1/admin/tickets/tkt_zyy8t2dwma2nprbt/messages" \
  -H "Authorization: Bearer ptat_9f2c1a44_3b8e7d2f5c9a1b4e6d8f0a2c4e6b8d1f" \
  -H "Propper-Workspace: acme"
```

## Response

```json
{
    "data": [
        {
            "object": "ticket_message",
            "id": "tmsg_w8aesu7f9p4v4akd",
            "ticket_id": "tkt_zyy8t2dwma2nprbt",
            "from": "system",
            "author": null,
            "body": "The assistant handed this conversation to the team.",
            "image_url": null,
            "image_name": null,
            "internal": false,
            "streaming": false,
            "via_email": false,
            "event": "handoff",
            "read_at": null,
            "created_at": "2026-08-18T01:27:19+00:00"
        },
        {
            "object": "ticket_message",
            "id": "tmsg_ebt6zayzphz6vras",
            "ticket_id": "tkt_zyy8t2dwma2nprbt",
            "from": "admin",
            "author": "Ios Tester",
            "body": "",
            "image_url": "/v1/admin/tickets/tkt_zyy8t2dwma2nprbt/messages/tmsg_ebt6zayzphz6vras/image",
            "image_name": "red.png",
            "internal": false,
            "streaming": false,
            "via_email": false,
            "event": null,
            "read_at": null,
            "created_at": "2026-10-15T22:20:00+00:00"
        }
    ],
    "meta": {
        "request_id": "req_01m50tw0vzfkbf76h61r45sp9w",
        "has_more": false,
        "next_cursor": null
    }
}
```

| Field        | Description |
| ------------ | ----------- |
| `from`       | `trader`, `admin` (the team), `ai` (the assistant) or `system` |
| `author`     | Who on the team wrote it, by name. Null for the trader, the assistant and the system, and for our own staff |
| `body`       | The text. Empty for a picture sent alone |
| `image_url`  | The [picture](/docs/api/admin/ticket-image) the message carries, on this API. Null without one |
| `image_name` | What the picture was called when it was sent |
| `internal`   | A note for the team. The trader never sees it |
| `streaming`  | Nothing sets it today. An answer the assistant is still writing arrives on the ticket's channel as `text_delta`, and lands here once it is done |
| `via_email`  | The trader answered from their mail client, without seeing the thread |
| `event`      | What a line of the system or a note stands for, see below. Null for an ordinary message |
| `read_at`    | When the trader read it, for what the team, the assistant and the system wrote where the trader can see it. Null until then, and always for the trader's own messages and for notes |

| Event                  | What happened |
| ---------------------- | ------------- |
| `handoff`              | The assistant handed the conversation to the team |
| `ai_failure_handoff`   | The assistant could not answer, so the ticket went to the team |
| `ai_command`           | The team's instruction to the assistant, kept as a note. See [ask the assistant](/docs/api/admin/ticket-ai) |
| `ai_command_failed`    | The assistant could not follow that instruction. A note, and nothing reached the trader |
| `ai_blocked`           | The team blocked the assistant while it was answering, which handed the ticket over |
| `ai_resumed_by_trader` | The trader called the assistant back in with `/ai` |
| `resolved`             | The team resolved the ticket |
| `idle_warning`         | The trader was warned that the quiet ticket is about to close |
| `auto_closed`          | The quiet ticket closed itself |

## Errors

| Status | Code                      | Meaning                                                                         |
| ------ | ------------------------- | ------------------------------------------------------------------------------- |
| 400    | `workspace_required`      | Asked by our own staff without naming a firm                                    |
| 401    | `invalid_token`           | Not a token, or the secret does not match                                       |
| 401    | `revoked_token`           | Signed out here or from another device                                          |
| 401    | `expired_token`           | Ninety days without use, or a set expiry passed                                 |
| 403    | `admin_access_required`   | This account no longer helps run the environment                                |
| 403    | `insufficient_scope`      | This device does not hold `admin:read`                                          |
| 403    | `workspace_not_permitted` | The `Propper-Workspace` header names an environment this account cannot act in |
| 403    | `forbidden`               | Without `view_tickets`                                                          |
| 404    | `resource_not_found`      | No ticket with that id in this firm                                             |