camelAI Documentation

API Reference

Events

The event stream: endpoints, cursors, frames, event types and run outcomes

View as Markdown · Docs index for coding agents

An agent's event stream says what it is doing as it does it: text as the model writes it, tool calls and their progress, questions it waits on, and files it presents. Use it for display. A run's result is the source of truth; events are for display. A stream can drop events (a restart, a slow reader, a frame too large), and the SDKs settle every run from its outcome, never from its events.

Read the stream

EndpointTokenMode
GET /v1/agents/{id}/eventsAn API key, or a browser tokenRead-only watcher
GET /clients/{id}/eventsThe agent's own token (the SDKs)The application's connection, which also carries tool calls to the application
Option
Last-Event-ID: <cursor>Resume after this event id
?snapshot=1Where the stream can't replay what you missed, send a snapshot of the running turn first instead of a 409. Watchers get one by default
?poll=1&wait=NAnswer once, as JSON ({cursor, events: [{id, data}]}), instead of streaming. wait is at most 25 seconds. For clients that can't hold a stream open

The response is text/event-stream. Each frame is data: JSON, and most have an id: line, the cursor. A : heartbeat comment arrives about every 5 seconds, so a stream silent for much longer is dead: reconnect with the last cursor.

event: ready
data: {"version":5,"agentId":"client_...","connection":"..."}

id: 1727520000000123
data: {"type":"event","requestId":"8c1...","event":{"type":"agent_start"}}

camelRun buffers each agent's last 512 events (at most 2 MiB) for replay. A cursor behind that gets a snapshot, or 409 REPLAY_GAP without one. Nothing durable is lost in a gap, only display events; recover settled outcomes from GET /v1/agents/{id}/state and /history.

Frames

Frame
{"type": "event", "requestId", "event"}One event. requestId is the run it belongs to
{"type": "response", "id", "outcome"}A request settled. See Run outcomes
{"type": "snapshot", "cursor", "requestId", "turn"}The running turn, sent in place of what couldn't be replayed: messages it finished and the partial message streaming now

Event types

New types may be added, so ignore ones you don't know.

Runs and messages

TypeWhen
turn_openedA model run begins. index is where its first message will sit in history
agent_start, agent_endThe model loop starts and ends
turn_start, turn_endA model round (one response and its tool calls) starts and ends
message_startA message begins: the user's, an assistant response, or a tool result
message_updateThe streaming assistant message grew. Carries only the delta, in assistantMessageEvent
message_endA message finished, as history keeps it
message_retractedA response was taken back (before a retry), and the message at index is gone

assistantMessageEvent.type is text_delta for reply text, thinking_delta for reasoning, and toolcall_delta for a tool call's arguments as partial JSON, each with its start and end. Fold deltas into the message from its message_start; @camelai/run/watch does this for you.

Tools, people and the runtime

TypeWhen
tool_execution_startA tool call starts: toolCallId, toolName, args
tool_execution_updateA tool reported progress (partialResult), at most every 250 ms per call
tool_execution_endA tool call finished: result, isError
file_presentedThe agent presented a file, with a signed download url
input_requiredThe turn waits on a person. input has id, kind (question, approval, form or url), message, detail and expiresAt
input_resolvedAn input settled: answered, declined, cancelled, expired or superseded

Sub-agents

See Multi-agent. These events reach only streams that ask for them (?subagents=1, or subagents: true in the SDKs).

TypeWhen
subagent_startA delegate call started its sub-agent: toolCallId, agentId, requestId, name, depth
subagent_eventOne of the sub-agent's events (event), its streamed text left out; a grandchild's arrive nested in its parent's
subagent_endThe sub-agent's run ended (status: completed, input_required, failed; error?), or its parent stopped waiting
auto_retry_start, auto_retry_endA transient provider failure is being retried
compaction_start, compaction_endThe conversation is being summarized to fit the model's context
turn_resumedThe node running this turn was lost, and it continues on another

Run outcomes

A prompt's outcome.result, from a response frame or GET /v1/agents/{id}/requests/{requestId}:

Field
replyThe final assistant message's text
outputA prompt sent with output: the answer, which fits its schema. See Structured output
replyIndexThat message's index in the agent's history
messagesHow many messages the history holds after the run
errorThe model's error (a provider refusal after retries), or null
codeA stable name for error, where there is one: model_key_missing (no key for the agent's model), model_key_invalid (the provider refused the key), output_missing (a structured run ended without its output)
stoppedWhy the run stopped early: input_required or spend_limit
inputsWhen stopped is input_required, the pending inputs
filesFiles the run wrote (at most 100)
presentedFiles the run presented (at most 20)
toolErrorsTool calls that didn't complete. See Tool errors
toolCallsEvery tool call the run made, in order, the first 100: {tool, toolCallId?, innerCallId?, ok, code?, agentId?}; agentId is the sub-agent a delegate call ran. A call from js_exec's code has js_exec's toolCallId and its own innerCallId; js_exec itself isn't listed. When ok is false, code is a toolErrors code, tool_error (the tool answered with an error), input_required (it waits on a person; the run that resumes it lists it again) or aborted. Arguments and results are left out: read them from history by toolCallId
sourceErrorsTool sources (MCP servers, OpenAPI specs) that couldn't be listed
usageWhat its model responses used; subagentCostUsd, what its sub-agents spent

outcome.error without a result means camelRun couldn't carry the request out, and uncertain: true means a restart cut it short so nobody can tell whether its work took effect. An ended request record (state: "completed") also puts status (completed, input_required or failed), error and stopped on top, so a failed run reads as failed without looking inside result. GET /v1/agents/{id}/requests/{requestId}?wait=25 waits for a running request (up to 25 seconds): it answers as soon as the request settles, or when the wait ends with it still running, so you can ask again.

The SDKs turn this into a typed Run: status (completed, input_required or failed), text, inputs, error, files, toolErrors, toolCalls and sourceErrors. A browser token never sees a run's result, toolCalls included: only whether and why it stopped.