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

# List messages

> GET /v1/conversations/{id}/messages — page through a conversation's message thread

Returns a conversation's messages, ordered by `createdAt` ascending (oldest first) with cursor pagination. There is no cap on thread length — keep following `nextCursor` until `hasMore` is `false`.

```http theme={null}
GET /v1/conversations/{id}/messages
```

## Authentication

Bearer token in the `Authorization` header. See [Authentication](/api-reference/authentication).

```http theme={null}
Authorization: Bearer rfl_live_...
```

## Path parameters

<ParamField path="id" type="string" required>
  Conversation ID prefixed with `conv_`.
</ParamField>

## Query parameters

<ParamField query="limit" type="integer" default="20">
  Number of messages per page. Range `1`–`100`.
</ParamField>

<ParamField query="startingAfter" type="string">
  Opaque cursor from the previous response's `nextCursor`. See [Pagination](/api-reference/pagination).
</ParamField>

## Response

Returns a [list envelope](/api-reference/pagination#response-shape) containing `Message` objects.

### The Message object

<ResponseField name="id" type="string">
  Unique identifier prefixed with `msg_`.
</ResponseField>

<ResponseField name="object" type="string">
  Always `"message"`.
</ResponseField>

<ResponseField name="conversationId" type="string">
  The owning conversation's ID.
</ResponseField>

<ResponseField name="type" type="string">
  Message medium. One of `chat`, `email`.
</ResponseField>

<ResponseField name="role" type="string">
  Who sent the message. One of `user` (a contact), `assistant` (Replyful AI), `human` (a teammate), `system`.
</ResponseField>

<ResponseField name="author" type="object | null">
  The resolved sender. `null` for system messages.

  <Expandable title="properties">
    <ResponseField name="type" type="string">One of `contact`, `user` (a teammate), `ai_agent`.</ResponseField>
    <ResponseField name="id" type="string">The author's ID (`con_…`, `user_…`, or `agent_…`).</ResponseField>
    <ResponseField name="name" type="string | null">Display name.</ResponseField>
    <ResponseField name="email" type="string | null">Email address. Always `null` for `ai_agent`.</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="content" type="string | null">
  Plain-text body. `null` if the message has no text content.
</ResponseField>

<ResponseField name="attachments" type="array">
  Non-inline files attached to the message.

  <Expandable title="properties">
    <ResponseField name="id" type="string">Attachment ID.</ResponseField>
    <ResponseField name="filename" type="string">Original filename uploaded by the sender.</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="createdAt" type="string">
  ISO 8601 UTC timestamp of when the message was recorded.
</ResponseField>

## Examples

### Page through a thread

<CodeGroup>
  ```bash curl theme={null}
  curl "https://api.replyful.com/v1/conversations/conv_x7Kw2mQb9tRfN4c/messages?limit=50" \
    -H "Authorization: Bearer rfl_live_..."
  ```

  ```ts Node.js theme={null}
  const res = await fetch(
    "https://api.replyful.com/v1/conversations/conv_x7Kw2mQb9tRfN4c/messages?limit=50",
    { headers: { Authorization: `Bearer ${process.env.REPLYFUL_API_KEY}` } },
  );
  const { data, hasMore, nextCursor } = await res.json();
  ```

  ```python Python theme={null}
  import os, requests

  res = requests.get(
      "https://api.replyful.com/v1/conversations/conv_x7Kw2mQb9tRfN4c/messages",
      headers={"Authorization": f"Bearer {os.environ['REPLYFUL_API_KEY']}"},
      params={"limit": 50},
  )
  res.raise_for_status()
  body = res.json()
  ```
</CodeGroup>

```json Response theme={null}
{
  "object": "list",
  "data": [
    {
      "id": "msg_k2Wq7xTb9mRf4cN8jP",
      "object": "message",
      "conversationId": "conv_x7Kw2mQb9tRfN4c",
      "type": "email",
      "role": "user",
      "author": {
        "type": "contact",
        "id": "con_9dKmT3xWq6bYrN8cJfA2eH4gp",
        "name": "Ada Lovelace",
        "email": "ada@example.com"
      },
      "content": "Hi, I'd like a refund on my last order. Order #4821.",
      "attachments": [
        { "id": "ea_6tXw2qKb8mRf3cN", "filename": "receipt.pdf" }
      ],
      "createdAt": "2026-04-29T14:30:00Z"
    },
    {
      "id": "msg_q8Tb3xWk2mRf7cN4jD",
      "object": "message",
      "conversationId": "conv_x7Kw2mQb9tRfN4c",
      "type": "email",
      "role": "assistant",
      "author": {
        "type": "ai_agent",
        "id": "agent_w2Kq7xTb9mRf4",
        "name": "Support Bot",
        "email": null
      },
      "content": "Thanks Ada — I can see order #4821 here. Could you confirm the email used at checkout so I can look up the payment?",
      "attachments": [],
      "createdAt": "2026-04-29T14:32:00Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null,
  "url": "/v1/conversations/conv_x7Kw2mQb9tRfN4c/messages"
}
```

## Error responses

| Status | `error.code`              | When                                                                |
| ------ | ------------------------- | ------------------------------------------------------------------- |
| `401`  | `missing_api_key`         | The `Authorization` header is absent.                               |
| `401`  | `invalid_api_key`         | The token is malformed, unknown, or archived.                       |
| `404`  | `conversation_not_found`  | The conversation does not exist or belongs to another organization. |
| `422`  | `invalid_path_parameter`  | The `id` path parameter is not a valid `conv_…` ID.                 |
| `422`  | `invalid_query_parameter` | A query parameter failed validation.                                |
| `422`  | `invalid_cursor`          | The `startingAfter` cursor could not be decoded.                    |
| `429`  | `too_many_requests`       | Rate limit exceeded. Honor `Retry-After`.                           |

See [Errors](/api-reference/errors) for the full error envelope reference.
