Local session HTTP API.
Start a loopback Code server, create trusted sessions, submit messages, follow resumable events, and answer permissions using the local session API.
kaitoi code serve exposes ongoing Code sessions to local clients. It runs on
IPv4 loopback and uses its own bearer token. It is separate from the hosted
Kaitoi API and the recipe Runtime API. This guide
covers protocol kaitoi-code-serve/1 in CLI 0.9.0.
Start and discover the server
kaitoi code serve
# Or choose a fixed port:
kaitoi code serve --port 8767
The default port 0 chooses a free port. Binding is always 127.0.0.1;
there is no host option. You can set --provider, --model, and
--token-file PATH.
Without a token file, the server creates a local token and writes its address
and token to ~/.kaitoi/code/serve/<pid>.json, then removes the record when it
stops. Protect that record. With --token-file, supply an existing protected
token file; no discovery record is written, so configure the client address
and token explicitly.
Every route—including /v1/health—requires Authorization: Bearer <local-token>.
The Host must be 127.0.0.1:<port> or localhost:<port>, and requests must have
no Origin header. There is no browser CORS or WebSocket interface. An account
API key is not a substitute for this local control token.
Create a session and follow one turn
Create a session with an absolute workspace and an explicit trust level:
{
"workspace": "/absolute/path/to/project",
"trust": "read",
"mode": "plan"
}
Trust is none, read, or edits. Optional fields include provider, model,
resume, and commands. Command availability is not blanket command approval.
POST a message such as {"text":"Explain this project"} to the returned
session's messages route. The 202 response gives turnId and after; use the
cursor to follow that turn without missing its first events.
A client creates a served session, submits a message, and follows its events from the returned cursor. Permission decisions travel back on a separate request. The turn ends, but the session-wide SSE connection can remain open for the next turn. Cancellation stops current work; closing deletes the served session while preserving its saved log.
A continuous session event line crosses the end of turn 1 and the start of turn 2. Reconnecting after the last received ID follows that existing stream, rather than submitting the message again.
Route reference
Paths below are relative to the local server. Replace {id} and
{permissionId} with returned identifiers.
| Method and path | Purpose |
|---|---|
GET /v1/health |
Authenticated health, version, and protocol. |
GET /v1/sessions |
List open served sessions. |
POST /v1/sessions |
Create; returns 201. |
GET /v1/sessions/{id} |
Inspect a session. |
DELETE /v1/sessions/{id} |
Stop and close it; saved log remains. |
POST /v1/sessions/{id}/messages |
Send {text, images?}; image paths are absolute; returns 202. |
GET /v1/sessions/{id}/events |
Session-wide SSE, with after query or Last-Event-ID. |
POST /v1/sessions/{id}/permissions/{permissionId} |
Answer a pending request with {decision}. |
POST /v1/sessions/{id}/cancel |
Request current-turn cancellation with {}. |
POST /v1/sessions/{id}/mode |
Set {mode} for the next turn: plan, build, or crew. |
Without a cursor, the event stream starts with future events. It sends heartbeat
comments every 15 seconds and can span several turns. turn.end marks a single
turn's end, so a client should not wait for the whole SSE connection to close.
Handle permissions and lifecycle
Answer only actual pending permission IDs and use their advertised decisions.
Most offer allow_once, allow_session, and deny; Crew and media accept only
one-call approval or denial. Pending requests time out after 600 seconds.
A session runs one turn at a time. New messages can return 409 turn_running
or turn_stopping. Cancel or close stops the command tree before another turn
can start; cancellation may remain in progress. It is not a rollback of work
already completed. Use the SDK for a synchronous Python
client that handles the per-turn stream boundary.
Last reviewed against Kaitoi on 24 October 2026. View this page as Markdown