Conversations and messages
Developer·3 minutes to read
Verified against docs/superpowers/specs/2026-09-15-p4-a-typed-http-api-openapi-webhooks.md §4.3, §4.4, §5.1, §5.2, §6; re-verified against the code at close-out.
What it does
The conversations the key's person is in, and the messages inside one: read a thread, page through it, and read the cards each message carries (F-CONV-1 to F-CONV-12 — the sidebar row, the composer, the append-only transcript, the card catalogue, the five-part result, and the per-message request id).
Authentication
An organisation API key of scope read or wider. There is no write on this page: a message is sent by starting a run (Runs). Conversations are read under the key's person, and the product shows a person only their own: a group conversation they are not in is not_found here, exactly as it is absent there. See Authentication and keys.
Request
GET /conversations
<!-- generated:begin GET /conversations --> <!-- generated:end GET /conversations -->GET /conversations/{id}
<!-- generated:begin GET /conversations/{id} --> <!-- generated:end GET /conversations/{id} -->GET /conversations/{id}/messages
<!-- generated:begin GET /conversations/{id}/messages --> <!-- generated:end GET /conversations/{id}/messages -->GET /conversations takes an optional botId and lists the person's own direct and group conversations. GET /conversations/{id}/messages takes limit and cursor like every list, and also afterSeq: pass the sequence number you last read and the page starts after it, oldest first. That is the shape to poll a thread with.
Message bodies are the app's own redacted projections. This API adds no unredacted reader, and the run ledger — the hash-chained record of model and tool calls (F-RUN-3) — is not exposed here at all.
curl -sS "$BOTSEON_API_ORIGIN/api/v1/conversations?limit=20" \
-H "authorization: Bearer $BOTSEON_API_KEY" \
-H "accept: application/json"
const origin = process.env.BOTSEON_API_ORIGIN;
const auth = { authorization: `Bearer ${process.env.BOTSEON_API_KEY}`, accept: 'application/json' };
const list = await fetch(`${origin}/api/v1/conversations?limit=1`, { headers: auth });
if (!list.ok) throw new Error(`conversations: ${list.status}`);
const page = (await list.json()) as { data: Array<{ id: string; title: string | null }> };
const first = page.data[0];
if (first) {
const messages = await fetch(
`${origin}/api/v1/conversations/${first.id}/messages?afterSeq=0&limit=50`,
{ headers: auth },
);
if (!messages.ok) throw new Error(`messages: ${messages.status}`);
const thread = (await messages.json()) as { data: Array<{ seq: number; role: string }> };
for (const message of thread.data) console.log(message.seq, message.role);
}
Response
{
"data": [
{
"id": "6b7c8d9e-0f1a-4b2c-8d3e-4f5a6b7c8d9e",
"seq": 41,
"role": "member",
"kind": "message",
"body": { "cards": [{ "kind": "message", "text": "Here are the release notes." }] },
"requestId": "…",
"authorUserId": "8c1d4e5f-2a3b-4c5d-6e7f-8a9b0c1d2e3f",
"botId": null,
"createdAt": "2026-09-18T08:00:00.000Z"
}
],
"nextCursor": null
}
seq is the message's place in the thread and is what afterSeq takes. requestId is the id the app shows beside a message, and quoting it in a support request is what makes an answer possible.
Errors
| Status | Code | When | What to do |
|---|---|---|---|
| 401 | unauthenticated | The credential is missing, refused, or below read. | Check the key. |
| 404 | not_found | No such conversation, or the key's person is not in it. | List first; a group the person is not a member of never appears. |
| 400 | invalid_request | limit, cursor or afterSeq is outside its bounds. | limit is 1 to 200; afterSeq is a non-negative integer. |
| 429 | rate_limited | A bucket is full. | Wait for Retry-After. |
Rate limits
Every call on this page is counted against two one-minute buckets: 300 requests per key and 1 000 per organisation. A full bucket answers rate_limited with Retry-After: 60; wait that many seconds and retry. Failed authentication is counted separately, against the caller's address, so a working key's allowance is never spent by somebody else's bad credential. See Envelopes, errors, idempotency and paging.
Last verified against build c0f77aa.