---
title: "List conversation messages"
method: GET
path: "/api/v1/conversations/{id}/messages"
tags: ["conversations"]
---

# List conversation messages

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

## Request

- Base URL: `https://app.blueticked.com`
- URL: `https://app.blueticked.com/api/v1/conversations/{id}/messages`
- Auth: HTTP bearer

Read the message history (inbound and outbound) for one conversation, oldest first.

This is how you read inbound replies; for a real-time feed subscribe to the message.received webhook instead of polling.

Messages come oldest first and the cursor walks forward, so ?limit=1 returns the first message in the conversation, not the newest reply. Take the newest reply from the message.received webhook (it carries message_id and the body), or page through to the end; the SDK's messagesAll() walks every page for you.

Always branch on `has_media`, not on `body`. A customer who sends a photo or a PDF with no caption produces an empty `body`. Without `has_media` that is indistinguishable from an empty text message, and the attachment is silently missed.

`media_status` is `pending` while we fetch the asset from WhatsApp, `downloaded` once it is stored, and `failed` when the download permanently gave up. Treat `failed` as terminal: WhatsApp's media handle has expired by then and the asset cannot be recovered. Poll or wait for the webhook rather than assuming a null `media_url` means there is no attachment.

To fetch a file, call `media_download_path` with your API key (GET /api/v1/conversations/{id}/messages/{messageId}/media): it answers with a signed URL that works for five minutes. `media_download_path` is null when the message has no file.

`media_url` is deprecated and will be removed on 2027-04-15: use `media_download_path`. Until then it is a signed link minted for each read that works for one hour (it was the stored link before, which for files you sent stayed valid for seven days). Never store it: read it again, or call the download path, when you need the file.

## Path parameters

- `id` string, required

## Query parameters

- `limit` integer
- `cursor` string

## Response `200`

Success

- object
  - `data` object[], required
    - `id` string, uuid, required
    - `direction` 'inbound' | 'outbound', required
    - `body` string, required
    - `status` string, required
    - `is_draft` boolean, required
    - `created_at` string, date-time, required
    - `updated_at` string, date-time, required
    - `has_media` boolean, required
    - `media_type` string, nullable
    - `media_mime` string, nullable
    - `media_filename` string, nullable
    - `media_status` string, nullable
    - `media_download_path` string, nullable
    - `media_url` string, nullable
  - `next_cursor` string, nullable, required
  - `has_more` boolean, required
  - `request_id` string, required

## Other responses

- `400`: Validation failed
- `401`: Unauthorized: missing or invalid API key
- `402`: Insufficient wallet balance
- `403`: Forbidden: scope, plan limit, or suspension
- `404`: Not found
- `409`: Conflict (idempotency-key reuse or resource in use)
- `422`: Understood but refused: the request is well-formed, the action is not permitted for this resource
- `429`: Rate limited
- `500`: Internal error
- `502`: An upstream provider refused or failed the request
- `503`: Channel or send queue unavailable

---

[API](https://skmtc.dev/blueticked/apis/blueticked-api.md) · [All operations](https://skmtc.dev/blueticked/apis/blueticked-api/llms.txt) · [OpenAPI document](https://skmtc.dev/blueticked/apis/blueticked-api/revisions/07132284f4ff?raw)
