# List thread messages

Read a thread's durable message history. This endpoint includes chat, thinking, tool, and status messages; use kind and role to distinguish them.

`GET /api/threads/{id}/messages`

Required permission: `thread:read`.

## Parameters

| Name | In | Type | Required | Description |

| --- | --- | --- | --- | --- |

| `id` | path | `string` | Yes | The Hoplite thread ID. |

| `cursor` | query | `string` | No | Base64url-encoded JSON containing the id and createdAt of the oldest message from the previous page, for example {"id":"msg_…","createdAt":"2026-08-10T08:04:00.000Z"}. |

| `limit` | query | `integer` | No | Maximum number of messages to return. Values above 500 are clamped to 500. |

| `activityLimit` | query | `integer` | No | Maximum number of activity messages to include. Values above 5000 are clamped to 5000. |

## Example request

```bash
curl --request GET \
  --url https://api.hoplite.sh/api/threads/thr_01JQ7B/messages \
  --header "X-Api-Key: $HOPLITE_API_KEY"
```

## 200 response

A page of messages.

Content type: `application/json`. Response type: `MessagesResponse`.

| Field | Type | Required | Constraints | Description |
| --- | --- | --- | --- | --- |
| `ok` | `boolean` | Yes | constant true | — |
| `messages` | `Message[]` | Yes | — | — |
| `messages.id` | `string` | Yes | — | — |
| `messages.threadId` | `string` | Yes | — | — |
| `messages.runId` | `string \| null` | No | — | — |
| `messages.queuedAfterRunId` | `string \| null` | No | — | — |
| `messages.authorUserId` | `string \| null` | No | — | — |
| `messages.author` | `UserSummary \| null` | No | — | — |
| `messages.author.Option 1` | `UserSummary` | No | — | — |
| `messages.author.Option 1.id` | `string` | Yes | — | — |
| `messages.author.Option 1.name` | `string` | Yes | — | — |
| `messages.author.Option 1.image` | `string \| null` | No | — | — |
| `messages.author.Option 2` | `null` | No | — | — |
| `messages.role` | `string` | Yes | user · assistant · system · tool | — |
| `messages.kind` | `string` | Yes | chat · thinking · tool · status | — |
| `messages.content` | `string` | Yes | — | — |
| `messages.createdAt` | `string<date-time>` | Yes | — | — |
| `messages.toolCallId` | `string` | No | — | — |
| `messages.toolName` | `string` | No | — | — |
| `messages.checkpointId` | `string` | No | — | — |
| `messages.metadata` | `object \| null` | No | — | — |
| `messages.metadata.[key]` | `object` | No | — | An additional property with any JSON value. |
| `hasMore` | `boolean` | Yes | — | — |
| `activityHasMore` | `boolean` | No | — | — |
| `nextCursor` | `string \| null` | No | — | Opaque backward cursor for the next page of persisted messages. |

```json
{
  "ok": true,
  "messages": [
    {
      "id": "msg_01JQ7D",
      "threadId": "thr_01JQ7B",
      "role": "assistant",
      "kind": "chat",
      "content": "Implemented the checkout analytics event and added tests.",
      "createdAt": "2026-08-10T08:04:00.000Z"
    }
  ],
  "hasMore": false
}
```

## Response headers

| Header | Type | Statuses | Description |

| --- | --- | --- | --- |

| `RateLimit` | `string` | `200`, `400`, `401`, `402`, `403`, `429`, `500`, `501`, `503` | Current ingress policy, remaining requests, and seconds until reset, for example `"ingress";r=599;t=60`. |

| `RateLimit-Policy` | `string` | `200`, `400`, `401`, `402`, `403`, `429`, `500`, `501`, `503` | Ingress quota and window, for example `"ingress";q=600;qu="requests";w=60`. |

| `X-RateLimit-Limit` | `integer` | `200`, `400`, `401`, `402`, `403`, `429`, `500`, `501`, `503` | Maximum requests allowed in the current rolling window. |

| `X-RateLimit-Remaining` | `integer` | `200`, `400`, `401`, `402`, `403`, `429`, `500`, `501`, `503` | Requests remaining in the current rolling window. |

| `X-RateLimit-Reset` | `integer` | `200`, `400`, `401`, `402`, `403`, `429`, `500`, `501`, `503` | Unix timestamp when the current rolling window resets. |

| `x-request-id` | `string` | `200`, `400`, `401`, `402`, `403`, `429`, `500`, `501`, `503` | Request correlation ID. |

| `Retry-After` | `integer` | `429`, `503` | Seconds until another request should be attempted. |

| `X-Retry-After` | `integer` | `429`, `503` | Legacy retry delay in seconds. |

## Status codes

- `200` — A page of messages.

- `400` — The limit, activity limit, or cursor is invalid.

- `401` — The API key is missing, invalid, or lacks the operation's permission.

- `402` — The organization does not have access to the requested product capability.

- `403` — The key's current user no longer has the required organization membership or role.

- `429` — The API-key rate limit has been exceeded.

- `500` — The API encountered an unexpected failure.

- `501` — The deployment has not configured the requested capability.

- `503` — A required API dependency, including the rate limiter, is unavailable.