Skip to content

Server Routes ​

Every request AgentServerClient makes, so you can build a compatible backend, debug a proxy, or call the API yourself. Paths are relative to baseUrl (for example /api/agents). The same shape is used by a Cognipeer Console portal (/api/portal/{key}), which also serves the embed session routes below.

The routes are served by @cognipeer/agent-server; the server-side reference is Endpoints.

Requests and errors ​

Every request carries the headers built from authorization, headers and getHeaders (the last one wins), and Content-Type: application/json where there is a body. Requests without a body (cancel, export, file content, the event stream) send no Content-Type.

A failed request rejects with an Error whose message is the server's and which carries:

FieldFrom
statusThe HTTP status
codeerror.code, else error.type, else a top-level code of the answer
detailserror.details when it is an object (activeRunId, pendingActionIds, spentUsd / maxCostUsd, ...)

The message is read from error.message, else error (a string), else message. Codes that have a localized string are listed in Error codes.

json
{ "error": { "code": "conversation_busy", "message": "A turn is already running", "details": { "activeRunId": "run_x1" } } }

Agents ​

RequestAnswer
GET /agents{ "agents": AgentInfo[] }. Chat reads capabilities.attachments, capabilities.approvalArgsEdit, capabilities.budget and metadata (including starters, welcome, experience) from here
GET /agents/:agentIdAn AgentInfo

Conversations ​

RequestBody / queryAnswer
GET /conversations?agentId&userId&limit&offset{ conversations, total, limit, offset, hasMore }. Items may carry runtime, budget, spentUsd, pinned / metadata.pinned
POST /conversations{ agentId, title?, userId?, metadata? }{ conversation }
GET /conversations/:id?before=<messageId>&limit=<n>{ conversation, messages, pendingActions?, messagesPage? }. messages is the newest page (100 by default); messagesPage is { limit, order, hasMoreBefore, hasMoreAfter }; before returns the page just older than that message. conversation carries runtime, budget, spentUsd, continuable
PATCH /conversations/:id{ title? }, { pinned }, { budget } (null removes it), { metadata? }The updated conversation (or { conversation }). A budget change answers with the runtime it is in now and continuable
DELETE /conversations/:idNo body is needed (a 204 is accepted)
POST /conversations/bulk-delete{ ids } (at most 100){ results: [{ id, status: "deleted" | "not_found" | "busy" | "error", message? }], deleted: <count> }

runtime is { status: "idle" | "running" | "requires_action" | "budget_paused", stopReason?, lastEventSeq?, pendingActionIds?, activeRunId? }. A runtime or budget that is not well-formed is dropped by the client.

Messages and turns ​

RequestBodyAnswer
GET /conversations/:id/messages?limit&offset&order=asc|desc{ messages, total, limit, offset, hasMore }
POST /conversations/:id/messages{ message, files?, fileIds?, metadata?, stream? }JSON { message, response, status?, pendingActions?, stopReason?, budget?, spentUsd?, warnings?, conversationTitle?, usage? }, or with stream: true a Server-Sent Events stream
POST /conversations/:id/messages/:messageId/regenerate{ stream? }Like the line above, for the newest answer
POST /conversations/:id/messages/:messageId/edit{ content, fileIds?, stream? }Like the line above; later messages are dropped
POST /conversations/:id/messages/:messageId/feedback{ rating: "up" | "down" | null, reasons?, comment? }{ feedback? } with the stored rating
POST /conversations/:id/actions/:actionId{ resolution, stream? }The continuation, JSON or stream
POST /conversations/:id/continue{} or { stream: true }The continuation of a turn the usage limit stopped, JSON or stream
POST /conversations/:id/cancelnone200 (or 202 while the run winds down): { cancelled, runId?, settled?, cancelledActionIds?, stopReason?, runtime? }. Nothing to cancel is cancelled: false, not an error

An inline file in files is { name, content: <base64>, mimeType }; fileIds names files stored earlier with POST /files. The comment of a rating is at most 1000 characters in the UI.

A resolution is one of:

json
{ "type": "tool_approval", "decision": "allow", "approvedArgs": { "command": "ls -la" }, "rememberForSession": true }
{ "type": "tool_approval", "decision": "deny", "comment": "not that site" }
{ "type": "user_question", "answers": { "Which region?": "eu" } }
{ "type": "user_question", "answers": {}, "cancelled": true }
{ "type": "client_tool", "output": { "opened": 7 } }
{ "type": "client_tool", "error": "The host page did not answer" }

The stream ​

A streamed answer is text/event-stream; each data: line is a JSON event with a type, a timestamp and, on agent-server 0.3.0+, a seq:

data: {"type":"stream.start","conversationId":"c1","messageId":"m2","timestamp":1760000000000,"seq":12}

data: {"type":"stream.text","text":"Hello","timestamp":1760000000100,"seq":13}

data: {"type":"stream.done","conversationId":"c1","messageId":"m2","content":"Hello there","stopReason":"end_turn","timestamp":1760000000900,"seq":19}

The event types are stream.start, stream.text, stream.thinking, stream.tool_call, stream.tool_result, stream.progress, stream.action_required, stream.action_resolved, stream.error and stream.done; see Streaming. The reader accepts event: lines, CRLF line endings, multi-line data: and : comment lines (keep-alives).

Event log (reattach) ​

RequestAnswer
GET /conversations/:id/events/stream?after=<seq>&types=a,bA Server-Sent Events stream: the events after seq are replayed, then new ones arrive live. While a run is active the first frame is agent.message.snapshot (no id: line). agent.message.delta frames are live-only (no seq). Sent with Accept: text/event-stream

The client reopens a closed stream from the last seq with backoff. A 4xx answer other than 408 and 429 is final; 408, 429, 5xx and network errors are retried. See Running turns for the frames.

Export ​

RequestAnswer
GET /conversations/:id/export?format=md|json|pdf|html&tools=0|1The conversation as a file, with Content-Disposition naming it. A pdf request answered 501 export_format_unsupported is repeated as format=html (a print-friendly text/html page the chat prints)

Chat falls back to building Markdown and JSON from the messages on screen when the route answers 404, 405 or 501.

Files ​

RequestAnswer
POST /files?name=&conversationId=&mimeType= with the file's bytes as the body and its own Content-Type{ file }. A JSON file goes up as application/octet-stream with mimeType in the query
POST /files with { file: { name, content, mimeType }, conversationId?, metadata? }{ file } (the JSON form)
GET /files/:id{ file }
DELETE /files/:idRemoves the file
GET /files/:id/content?disposition=inline|attachmentThe bytes
GET /conversations/:id/files?limit=200{ files, total, maxFiles?, hasMore? }, oldest first

A file record is { id, name, mimeType, size, url?, expiresAt?, conversionStatus? }; conversionStatus is also read from conversion.status.

Embed session ​

Served by the portal for EmbedChat; not part of AgentServerClient:

RequestAnswer
POST <sessionUrl> with { token } and no cookies200 { sessionToken, expiresAt, user?: { name, email } }, or 4xx { error: { code, message, reference } }
DELETE <sessionUrl> with Authorization: Bearer <sessionToken>Revokes the session

All chat requests of an embedded chat then carry Authorization: Bearer <sessionToken>. See Embedding the Chat.

Studio · Pulse · Console · Agent SDK and more — the Cognipeer documentation hub