> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trychert.com/llms.txt
> Use this file to discover all available pages before exploring further.

# List messages

> GET /api/v1/chats/:id/messages — list messages in a chat, oldest first.

<Note>
  **Auth.** HMAC v1 or bearer token — see [Authentication](/api/authentication).
  **Status.** Live · **Idempotent:** Yes (read-only).
</Note>

Returns the 200 most recent messages in a chat, oldest first. This is a chat-scoped route: `{id}` is the `chat_id`, not a `message_id`. Chert combines saved conversation rows with durable inbound event rows so recent inbound messages still appear if a best-effort conversation append was missed. `has_more` is `true` if older messages were truncated. If the chat has no conversation yet, returns an empty array.

## Request

`GET /api/v1/chats/{id}/messages`

<RequestExample>
  ```bash cURL theme={null}
  curl "https://console.trychert.com/api/v1/chats/0f2d3c1a-8b4e-4f6a-90d2-1a3b4c5d6e7f/messages" \
    -H "x-chert-tenant: $TENANT" \
    -H "x-chert-signature: v1,$TS,$SIG"
  ```

  ```js Node.js theme={null}
  const ts = Math.floor(Date.now() / 1000)
  const body = ""
  const sig = crypto.createHmac("sha256", SIGNING_SECRET).update(`${ts}.${body}`).digest("hex")
  const res = await fetch(
    `https://console.trychert.com/api/v1/chats/${chatId}/messages`,
    {
      method: "GET",
      headers: {
        "x-chert-tenant": TENANT_SLUG,
        "x-chert-signature": `v1,${ts},${sig}`,
      },
    },
  )
  ```

  ```python Python theme={null}
  import hmac, hashlib, time, requests
  ts = int(time.time())
  body = ""
  sig = hmac.new(SIGNING_SECRET.encode(), f"{ts}.{body}".encode(), hashlib.sha256).hexdigest()
  r = requests.get(
      f"https://console.trychert.com/api/v1/chats/{chat_id}/messages",
      headers={
          "x-chert-tenant": TENANT_SLUG,
          "x-chert-signature": f"v1,{ts},{sig}",
      },
  )
  ```
</RequestExample>

## Response

<ResponseExample>
  ```json 200 OK theme={null}
  {
    "messages": [
      {
        "id": "46eb1003c8b54b7ea8f1c2b03e9a7d12",
        "chat_id": "0f2d3c1a-8b4e-4f6a-90d2-1a3b4c5d6e7f",
        "parts": [{ "type": "text", "value": "Hi Sam — got a minute?" }],
        "status": "delivered",
        "direction": "outbound",
        "created_at": "2026-05-01T12:00:00Z",
        "delivered_at": "2026-05-01T12:00:00Z",
        "read_at": null,
        "service": "imessage"
      },
      {
        "id": "abf41d8b6c2f4f9e9c1a3d5e7f8a9b0c",
        "chat_id": "0f2d3c1a-8b4e-4f6a-90d2-1a3b4c5d6e7f",
        "parts": [{ "type": "text", "value": "Sure, what time?" }],
        "status": "delivered",
        "direction": "inbound",
        "created_at": "2026-05-01T12:05:00Z",
        "delivered_at": "2026-05-01T12:05:00Z",
        "read_at": null,
        "service": "imessage"
      }
    ],
    "has_more": false
  }
  ```
</ResponseExample>

Message `id` values are opaque. For messages returned by this endpoint, pass
the listed `id` to message-scoped actions such as
[`POST /api/v1/messages/{id}/react`](/api/messages/react).

### Path parameters

<ParamField path="id" type="string" required>
  Chat identifier — the `chat.id` returned by [`POST /api/v1/chats`](/api/chats/create). Do not pass `message.id` or `convo_id` here.
</ParamField>

### Response fields

<ResponseField name="messages" type="object[]">
  Array of message rows, oldest first. Capped at the 200 most recent messages.
</ResponseField>

<ResponseField name="has_more" type="boolean">
  `true` when the underlying conversation has more than 200 messages and only the most recent 200 are included in this response. Cursor pagination on this endpoint is not yet supported.
</ResponseField>

## Errors

| Code   | HTTP | When                           |
| ------ | ---- | ------------------------------ |
| `2002` | 404  | Chat not found in your tenant. |

See the full [error code reference](/api/errors).

## See also

* [Send a message](/api/chats/send-message)
