List conversation messages (delta read by version cursor)
Returns the messages in this conversation with sequence_no > since, plus the
conversation’s current_version at the moment the response was generated. The
messages list is ordered by sequence_no ascending and is gap-free — if the
response contains messages with sequence_no 3, 4, 5, you have all three.
Polling pattern
since = 0
loop forever:
r = GET /agents/conversations/{id}/messages?since={since}
process r.messages # may be empty
since = r.current_version # use the head, not r.messages.last.sequence_no
wait briefly
current_version is the conversation’s head at the moment the response was generated
— using it as the next request’s since is the only way to ensure gap-free reads
even when new messages land between the time the response was generated and the time
your client processed it.
When messages get added
Messages are persisted in a single transaction by the workflow’s finalize step (when a run reaches a terminal state). You won’t see partial reads — a run that produces 4 messages either lands all 4 or none.
A run’s id appears in messages[].run_id, so you can correlate messages to the
runs that produced them.
What’s in content_blocks
Three discriminated variants:
text— plain text. The dominant variant.tool_use— the model requesting a tool call. Carriestool_use_id, the aliasedname, and the model’sarguments.tool_result— the result of a tool call, nested ascontent_blocksinside thetool_resultblock. Carries the originaltool_use_idplus anis_errorflag.
The same tool_use_id appears on the assistant’s tool_use block and on the
subsequent tool (or user, for resume payloads) turn’s tool_result block —
that’s how a UI matches them up.
Permission
agent_conversations resource with read verb.
Example: paging from scratch
curl "$API/agents/conversations/{id}/messages?since=0" \
-H "Authorization: Bearer $TOKEN"
# → { "current_version": 4, "messages": [ <4 entries> ] }
Then later:
curl "$API/agents/conversations/{id}/messages?since=4" \
-H "Authorization: Bearer $TOKEN"
# → { "current_version": 4, "messages": [] } # nothing new
Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Path Parameters
The conversation's UUID.
UUID identifying a conversation. Returned from POST /agents/conversations and used
in every other agent endpoint that operates on this conversation.
"bc2505b7-068d-44ed-8055-a6f6ffe54ab1"
Query Parameters
Return messages with sequence_no > since. Use the previous response's
current_version as the next request's since for gap-free incremental reads.
Default 0 returns the entire conversation from the beginning.
x >= 0Response
A page of messages with sequence_no > since, plus the current head version.
Returned by GET /agents/conversations/{id}/messages?since=N. Carries the
conversation's current head version (current_version) plus the requested page of
messages.
Polling pattern:
since = 0
loop:
response = GET /agents/conversations/{id}/messages?since={since}
process response.messages
since = response.current_version
sleep or wait for an eventThe current_version is the conversation's head at the moment the response was
generated — using it as the next request's since gives gap-free incremental
reads.
Monotonic per-conversation counter, bumped by 1 (or more) every time a successful run appends messages. Two roles:
- Delta cursor for
GET .../messages?since=N— fetch only messages withsequence_no > N. - Compare-and-swap token for
POST .../runs— setexpected_versionto the conversation's current head; if it doesn't match at run-creation time, you get a Version Conflict (HTTP 409).
Always start a new run cycle by reading the current version from
GET /agents/conversations/{id} rather than caching a value from earlier.
x >= 04
Messages with sequence_no > since. Empty array means no new messages — the
conversation is at version current_version but you've already seen everything.

