List messages
Return the customer-visible thread for the conversation the visitor token owns.
GET /api/v1/public/conversations/{id}/messagesReturns the customer-visible thread — the user's own messages and the agents' replies — in chronological order.
Authentication
Visitor token: Authorization: Bearer vt_….
The token is scoped to one conversation. It authorizes this endpoint only for its own conversation; the publishable key does not work here at all.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | uuid | The conversation_id returned when you started the conversation. |
The id must be the conversation the token was minted for. A well-formed UUID that the
token doesn't own comes back as 404, not 403 — deliberately, so that a token cannot
be used to probe which conversation IDs exist.
Query parameters
None. This endpoint has no after cursor: it returns the full customer-visible
thread on every call.
Polling clients: diff on message id
Because there's no cursor, a polling client should keep the set of message IDs it has
already rendered and append only the ones it hasn't seen. Any after-style parameter you
send is simply ignored — it won't filter anything, and you'll quietly re-render the whole
thread every tick if you assume otherwise.
Response
200 OK. data is an array of messages, oldest first.
| Field | Type | Description |
|---|---|---|
id | uuid | The message ID. Stable — use it to deduplicate while polling. |
author | string | customer or agent. |
body | string | The message text. |
created_at | string | ISO 8601 timestamp. |
{
"success": true,
"request_id": "6d7979ce52075b24979cd5b415319339",
"data": [
{
"id": "019f1c50-1111-7a80-91b2-c3d4e5f60718",
"author": "customer",
"body": "My order never arrived.",
"created_at": "2026-07-11T10:00:00.000000Z"
},
{
"id": "019f1c51-2222-7a80-91b2-c3d4e5f60718",
"author": "agent",
"body": "Sorry about that — let me look into it.",
"created_at": "2026-07-11T10:05:00.000000Z"
}
]
}Those four fields are the whole shape, and that is the point. No staff identifiers, no agent names, no internal notes, no metadata. Internal-only messages and system events are filtered out on the server, so what you get back is safe to render straight into a customer-facing UI — there is nothing here you need to strip first.
Errors
| Status | error.code | When |
|---|---|---|
400 | BAD_REQUEST | id is not a valid UUID (the conversation id is not a valid uuid). |
401 | UNAUTHORIZED | The visitor token is missing, unknown, expired, or revoked (the visitor token is invalid). |
404 | NOT_FOUND | The token does not own a conversation with this ID (conversation not found). |
429 | RATE_LIMITED | This token's budget is exhausted — 60 requests/minute by default, per token. |
A successful call slides the token's 30-day expiry window forward, so a conversation that's being polled never ages out. See Lifetime.
Examples
curl "{BASE_URL}/api/v1/public/conversations/019f1c4d-6e7f-7a80-91b2-c3d4e5f60718/messages" \
-H "Authorization: Bearer vt_example_7Jq4Xm2Pz9Ln6Rd3Wb5T"