kimi-code/docs/en/reference/server-api.md
Haozhe 15da84606a
Some checks are pending
CI / build (push) Waiting to run
CI / test (1) (push) Waiting to run
CI / test (2) (push) Waiting to run
CI / test (3) (push) Waiting to run
CI / test (4) (push) Waiting to run
CI / test (5) (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
CI / test-vscode-legacy (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
Nix Build / Check flake.nix workspace sync (push) Waiting to run
feat(kap-server): add workspace-grouped sessions view and lifecycle events (#3114)
GET /api/v2/sessions gains view=by_workspace: one request returns every
workspace with a matching session, each carrying its first group.page_size
sessions under the requested sort plus the workspace's full matching total,
with group-level page_token pagination (40922 on condition drift). Groups
key on the alias-canonical workspace id, so legacy split buckets of one
physical directory merge into a single group, matching the v1 alias
semantics. meta.has_prompt filters sessions by prompt presence (the v1
exclude_empty equivalent) in both views. The flat view and v1 routes stay
byte-compatible.

The global WS stream now fans out event.session.archived (live and cold
paths; payload carries the session id and workspace_id) and
event.workspace.created/updated/deleted, published by the core
IWorkspaceService on every mutation path including the implicit
createOrTouch on session creation.

kimi-inspect consumes the grouped projection as a single-column
workspace/session tree in the chat view; the session pane merges into the
right dock as the Session tab. The server API reference (en + zh) documents
the new parameters, the grouped response, and the new events.
2026-08-20 13:45:38 +08:00

25 KiB
Raw Blame History

Server API

The local server started by kimi web exposes two programmatic surfaces: a REST API (/api/v1, plus /api/v2/sessions) and a WebSocket event stream (/api/v1/ws). This page is the protocol reference for both. For how to start the server and its command-line options, see the kimi command reference; for an end-to-end walkthrough, see Local server and API.

The complete request/response schema of every endpoint is owned by the server's live specification documents: GET /openapi.json (OpenAPI) and GET /asyncapi.json (AsyncAPI). Both require authentication.

::: warning The REST and WebSocket APIs described on this page are experimental: interface stability is not guaranteed, and endpoints, fields, and event types may change in any release. When integrating, rely on the /openapi.json and /asyncapi.json documents served by your version. :::

Conventions

Address

The default address is http://127.0.0.1:58627. When the port is taken, the server retries with the next port (up to 100 times); use --port / --host to change the bind. Multiple instances can coexist under the same home directory; running instances register under ~/.kimi-code/server/instances/.

Authentication

All /api/* paths (including /openapi.json and /asyncapi.json) require the bearer token, except:

  • OPTIONS preflight requests
  • GET /api/v1/healthz (liveness probe)
  • Static web assets (non-/api/ paths)

How to carry it: REST uses the Authorization: Bearer <token> header; the WebSocket upgrade accepts the same header or the subprotocol kimi-code.bearer.<token>. Token generation and rotation are covered in Local server and API: Authentication.

Failed authentication returns HTTP 401 with envelope code 40101. On non-loopback binds, a source that fails authentication 10 times within 60 seconds is banned for 60 seconds, during which every request gets HTTP 429 (code 42901).

Response envelope

Every JSON response is wrapped in a uniform envelope:

{
  "code": 0,
  "msg": "success",
  "data": {},
  "request_id": "01JZX4A6E7M8V0R3Q0N2K2M5Q9"
}
  • code: the business outcome; 0 means success. See the error-code bands below.
  • data: the payload on success. Note that some "error" envelopes also carry a non-null data — for example, resolving an already-resolved approval returns 40902 with data.resolved set to false — so clients should check code first, then data.
  • request_id: a ULID for this request. Clients may supply one via the X-Request-Id header; invalid values are regenerated by the server.

The HTTP status is almost always 200; the business outcome lives in code. Exceptions:

Situation HTTP status
Authentication failure / rate limit 401 / 429
Provider created, provider catalog imported 201
Provider deleted 204
Binary/streaming endpoints 206 (Range) / 304 (ETag unchanged) where supported — capabilities differ per endpoint, see Binary and streaming endpoints
GET /api/v1/files/{file_id} download errors real 404 / 500 (still carrying an envelope body)

The 201 responses still carry the standard envelope (code 0) — only the status line follows the REST convention for resource creation. A 204 response has no body by definition, so a successful delete is reported by the status code itself.

Error codes

Error codes are grouped by band:

Band Meaning Examples
0 Success
400xx Bad request 40001 validation failed (details lists each field), 40003 provider is OAuth-managed
401xx Auth and readiness 40101 unauthorized, 40110 no provider configured, 40113 model not resolved
404xx Not found 40401 session, 40408 MCP server, 40409 file path
409xx State conflict 40901 session busy, 40902 approval already resolved, 40922 page conditions mismatch page_token
410xx Expired 41001 approval timed out, 41002 question timed out, 41003 temporary file expired
413xx Size or boundary exceeded 41302 file read over 10 MB, 41304 path escapes the session directory
429xx Rate limited 42901 auth-failure ban, 42902 too many fs watches
500xx Server internal error 50001 uncaught exception, 50003 persistence failure
6xxxx / 7xxxx / 8xxxx Tool runtime / LLM provider / MCP passthrough errors; msg carries the upstream text

Pagination

List endpoints come in two styles:

  • Cursor style: before_id / after_id (mutually exclusive) plus page_size (1100), responding with { items, has_more }. Used by the session list, message list, transcript, and others.
  • page_token: an opaque token (bound to a fingerprint of the query conditions), used by POST /api/v1/search and GET /api/v2/sessions. Changing any query condition mid-pagination invalidates the token: v2 returns 40922, search returns 40001. GET /api/v2/sessions also offers a stateless page page-number mode as an alternative.

REST endpoints

Endpoints are grouped by resource below. A :{action} suffix in a path is the action convention — POST to path:action on a single resource for non-CRUD operations (such as :fork and :archive on a session).

Server and metadata

Method and path Description
GET /api/v1/healthz Liveness probe; auth-exempt
GET /api/v1/meta Server version, capability map, server_id, experimental flags
POST /api/v1/shutdown Graceful shutdown (replies 200 first); mounted only on loopback binds

Login and usage

Method and path Description
GET /api/v1/auth Auth readiness snapshot
POST /api/v1/oauth/login Start the OAuth device-code login flow
GET /api/v1/oauth/login Poll the login flow state
DELETE /api/v1/oauth/login Cancel a pending login flow
POST /api/v1/oauth/logout Log out the managed provider
GET /api/v1/oauth/usage Plan usage and limits
GET /api/v1/oauth/userinfo Account profile

Config

Method and path Description
GET /api/v1/config Read the global config (secret fields redacted)
POST /api/v1/config Merge-patch the config; broadcasts event.config.changed

Models and providers

Method and path Description
GET /api/v1/models List configured model aliases
POST /api/v1/models/{model_id}:set_default Set the global default model
GET /api/v1/providers List providers
POST /api/v1/providers Create a provider (201)
GET /api/v1/providers/{provider_id} Read a provider (reveals the stored key)
PUT /api/v1/providers/{provider_id} Replace a provider
DELETE /api/v1/providers/{provider_id} Delete a provider (204)
POST /api/v1/providers/{provider_id}:refresh Refresh one provider's model metadata
POST /api/v1/providers:{action} Collection actions: refresh / refresh_oauth / import_catalog / import_registry
GET /api/v1/catalog/providers Browse the models.dev directory (server-proxied)
GET /api/v1/catalog/providers/{catalog_id} Read one directory entry

Sessions

Method and path Description
POST /api/v1/sessions Create a session (requires workspace_id or metadata.cwd)
GET /api/v1/sessions List sessions; cursor pagination with filters such as busy and archived_only
GET /api/v1/sessions/{session_id} Read one session
GET /api/v1/sessions/{session_id}/profile Read the session profile
POST /api/v1/sessions/{session_id}/profile Update title, metadata, agent config
POST /api/v1/sessions/{session_id}:{action} Session actions: fork / compact / undo / abort / btw / archive / restore
GET /api/v1/sessions/{session_id}/children List child sessions
POST /api/v1/sessions/{session_id}/children Create a child session (fork with a tag)
GET /api/v1/sessions/{session_id}/status Realtime status rollup
GET /api/v1/sessions/{session_id}/goal Current goal snapshot (null when none)
GET /api/v1/sessions/{session_id}/warnings Session-level warnings
POST /api/v1/sessions/{session_id}/export Export the session with diagnostics (zip stream, not enveloped)
GET /api/v1/sessions/{session_id}/snapshot Full snapshot for client rebuilds (with as_of_seq and epoch)

Messages and transcript

Method and path Description
GET /api/v1/sessions/{session_id}/messages Page messages (before_id / after_id / role)
GET /api/v1/sessions/{session_id}/messages/{message_id} Read one message
GET /api/v1/sessions/{session_id}/transcript Turn-paged transcript (requires agent_id); global state rides along unpaginated
GET /api/v1/sessions/{session_id}/transcript/ops Op-batch catch-up (since_seq); complete: false means a full refresh is needed
GET /api/v1/sessions/{session_id}/transcript/user-messages Turn-opening user inputs, unpaginated
GET /api/v1/sessions/{session_id}/transcript/plan ExitPlanMode plan content, path, and review outcome

Prompts

Method and path Description
GET /api/v1/sessions/{session_id}/prompts Active and queued prompts
POST /api/v1/sessions/{session_id}/prompts Submit a prompt (content-part array, optional model / permission-mode overrides)
POST /api/v1/sessions/{session_id}/prompts:steer Steer queued prompts into the active turn
POST /api/v1/sessions/{session_id}/prompts/{prompt_id}:abort Abort a running prompt
POST /api/v1/sessions/{session_id}/prompts/{prompt_id}:steer Steer one queued prompt

Approvals and questions

Method and path Description
GET /api/v1/sessions/{session_id}/approvals List approval requests (filter with status=pending)
POST /api/v1/sessions/{session_id}/approvals/{approval_id} Resolve an approval
GET /api/v1/sessions/{session_id}/questions List questions
POST /api/v1/sessions/{session_id}/questions/{question_id} Answer a question
POST /api/v1/sessions/{session_id}/questions/{question_id}:dismiss Dismiss a question

Background tasks

Method and path Description
GET /api/v1/sessions/{session_id}/tasks List background tasks
GET /api/v1/sessions/{session_id}/tasks/{task_id} Read a task (optional output preview)
POST /api/v1/sessions/{session_id}/tasks/{task_id}:cancel Cancel a task

Skills, tools, and MCP

Method and path Description
GET /api/v1/sessions/{session_id}/skills Per-session skill catalog
GET /api/v1/workspaces/{workspace_id}/skills Session-less skill catalog for a workspace
POST /api/v1/sessions/{session_id}/skills/{skill_name}:activate Activate a skill (starts a turn)
GET /api/v1/tools List tools of the effective agent
GET /api/v1/mcp/servers List MCP servers
POST /api/v1/mcp/servers/{mcp_server_id}:restart Restart an MCP server

Terminals

PTY terminal endpoints; mounted only on loopback binds.

Method and path Description
GET /api/v1/sessions/{session_id}/terminals List terminals
POST /api/v1/sessions/{session_id}/terminals Create a terminal
GET /api/v1/sessions/{session_id}/terminals/{terminal_id} Read a terminal (including scrollback)
POST /api/v1/sessions/{session_id}/terminals/{terminal_id}:close Close a terminal

Workspaces

Method and path Description
GET /api/v1/workspaces List registered workspaces
POST /api/v1/workspaces Register a workspace (idempotent on the root path)
PATCH /api/v1/workspaces/{workspace_id} Rename
DELETE /api/v1/workspaces/{workspace_id} Unregister (keeps on-disk content)
GET /api/v1/workspaces/{workspace_id}/trust Read the trust state
POST /api/v1/workspaces/{workspace_id}/trust Grant trust
POST /api/v1/workspaces/{workspace_id}/untrust Revoke trust

File system

In-session file operations go through POST /api/v1/sessions/{session_id}/fs:{action} with JSON bodies; actions are list / read / list_many / stat / stat_many / mkdir / search / grep / git_status / diff / open / open-in / reveal. In addition:

Method and path Description
POST /api/v1/workspace/fs:search Session-less workspace search (the body carries the workspace reference)
GET /api/v1/sessions/{session_id}/fs/{path}:download Download a session file (binary, see below)
GET /api/v1/fs:browse List host directories (folder picker)
GET /api/v1/fs:home The user's home directory and recent workspaces
GET /api/v1/fs:content Raw bytes of any host file (gated only by the token — be careful when exposing the port)
POST /api/v1/fs:mkdir Create a directory by absolute path

File uploads

Method and path Description
POST /api/v1/files Multipart upload (file field, optional name and expires_in_sec); returns file metadata
GET /api/v1/files/{file_id} Download (binary; errors use real HTTP statuses)
DELETE /api/v1/files/{file_id} Delete

Global search and misc

Method and path Description
POST /api/v1/search Cross-session full-text search; mode is terms (default) or literal (exact substring); page_token pagination
GET /api/v1/connections List live WebSocket connections
GET /api/v2/sessions Next-generation session list, see below
POST /api/v2/sessions:archive Batch-archive sessions, see below
POST /api/v2/sessions:restore Batch-restore archived sessions, see below
/api/v1/debug/* Reflection debug RPC; mounted only with --debug-endpoints on loopback, not a stable protocol

GET /api/v2/sessions

A next-generation session query for list views — filtering, sorting, and field groups all travel in query parameters:

Parameter Description
workspace.id Filter by workspace; repeatable
activity.status Filter by activity status: running / approval / question / failed / idle; repeatable
meta.updated_after Only sessions updated after this time (epoch milliseconds)
meta.updated_before Only sessions updated before this time (epoch milliseconds)
meta.archived true / false (default) / all
meta.has_prompt true keeps only sessions that carry a user prompt, false keeps only empty ones (the exclude_empty equivalent of GET /api/v1/sessions)
view flat (default) / by_workspace, see below
group.page_size Sessions returned per workspace under view=by_workspace: 1100, default 5 (up to 10000 with the id,archived projection); rejected without the grouped view (40001)
sort meta.updated_at_desc (default) / meta.updated_at_asc / meta.created_at_desc
include Comma-separated extra field groups; currently only git (branch and PR info, deduplicated per directory and cached for 60 seconds)
fields Comma-separated item projection; currently only id,archived, trimming each item to { id, archived } (select-all-matching flows). Not combinable with include=git (40001)
page_size 1100, default 50; up to 10000 with the id,archived projection. Under view=by_workspace it counts groups per page
page_token Pagination token from the previous page
page Stateless 1-based page number; mutually exclusive with page_token (40001 when combined)

Every response item carries the workspace, meta, and activity groups, plus git when include=git — or just { id, archived } under fields=id,archived. Every page additionally carries total, the size of the filtered set. The page token binds the first page's query conditions (including the projection); changing them mid-pagination returns 40922. page mode is a stateless alternative for jumping to arbitrary pages: every request is an independent snapshot, no token is minted, and next_page_token is always null.

With view=by_workspace the same filtered, sorted set is re-projected into per-workspace groups, so an overview client replaces one polling loop per workspace with a single request:

{
  "code": 0,
  "msg": "success",
  "data": {
    "groups": [
      {
        "workspace": { "id": "wd_my-app_a1b2c3d4e5f6", "cwd": "/Users/dev/my-app" },
        "sessions": [ { "id": "session_...", "workspace": { "id": "wd_my-app_a1b2c3d4e5f6", "cwd": "/Users/dev/my-app" }, "meta": { "title": "Fix the login page", "last_prompt": "adjust the button spacing", "created_at": 1787000000000, "updated_at": 1787000100000, "archived": false, "archived_at": null }, "activity": { "status": "idle" } } ],
        "total": 42
      }
    ],
    "total": 7,
    "has_more": true,
    "next_page_token": "eyJ2IjoxLCJmIjoi..."
  },
  "request_id": "req_..."
}

Each group carries the workspace's first group.page_size sessions under the requested sort plus total, the workspace's full matching-session count (for a "view all" entry). Only workspaces with at least one matching session appear; groups order by their first session's sort key, ties broken by workspace id. page and page_token paginate over groups (the outer total is the group count), with the same fingerprint binding: the token also covers view and the grouping parameters, so flipping them mid-pagination returns 40922.

POST /api/v2/sessions:archive and POST /api/v2/sessions:restore

Batch archive/restore for session-management views. The body is { "ids": ["session_..."] } — non-empty, at most 5000 unique ids (duplicates collapse). Live sessions go through the full lifecycle; cold sessions are patched on disk without being loaded.

Only a body validation failure fails the whole request (40001). Otherwise the response is per-item: data.results keeps the input order with { id, ok } or { id, ok: false, error } (an unknown id reports 40401 in its own item), plus succeeded / failed counts.

{
  "code": 0,
  "msg": "success",
  "data": {
    "results": [
      { "id": "session_a", "ok": true },
      { "id": "session_b", "ok": false, "error": { "code": 40401, "message": "session session_b does not exist" } }
    ],
    "succeeded": 1,
    "failed": 1
  },
  "request_id": "req_..."
}

WebSocket protocol

Connect

The only endpoint is ws://<host>:<port>/api/v1/ws; authentication happens at the upgrade request (see Authentication above). Once connected, the server immediately sends server_hello:

{
  "type": "server_hello",
  "timestamp": "2026-01-01T00:00:00.000Z",
  "payload": {
    "ws_connection_id": "conn_01JZX4...",
    "protocol_version": 2,
    "max_event_buffer_size": 1000,
    "capabilities": { "event_batching": false, "compression": false }
  }
}

Note that the server never sends heartbeats and never disconnects an idle connection — keepalive and reconnection are the client's job.

Control frames

Clients send JSON frames { "type", "id"?, "payload" }; every request frame gets an acknowledgement { "type": "ack", "id", "code", "msg", "payload" }, where code 0 means success.

Frame payload Description
subscribe { session_ids, cursors?, agent_filter? } Subscribe to session events; with cursors (per-session {seq, epoch}) the server replays missed durable events
unsubscribe { session_ids } Drop session subscriptions
subscribe_v2 { session_id, transcript, transcript_since? } Subscribe to transcript streams (the only transcript channel); transcript sets per-agent grades
unsubscribe_v2 { session_id, agent_ids? } Detach transcript streams; omitting agent_ids means the whole session
watch_fs_add / watch_fs_remove { session_id, paths, recursive? } Subscribe to / unsubscribe from file-change notifications (event.fs.changed)
client_hello { client_id } Handshake frame; the remaining fields are legacy compatibility

Events

Event frames look like { "type", "seq", "epoch"?, "volatile"?, "offset"?, "session_id"?, "timestamp", "payload" }, where type is the event type itself. Two delivery scopes:

  • Global events: sent to every established connection, no subscription needed — session.meta.updated, event.session.created, event.session.archived, event.session.work_changed, event.session.status_changed, event.workspace.*, event.config.*.
  • Session events: sent only to connections subscribed to that session, subject to agent_filter. Main families:
Family Main events
Turns turn.started, turn.ended, turn.step.started / completed / interrupted / retrying
Streaming text assistant.delta, thinking.delta (carry offset for alignment)
Tool calls tool.call.started, tool.call.delta, tool.progress, tool.result
Interactions event.approval.requested / resolved, event.question.requested / answered / dismissed
Subagents subagent.spawned / started / suspended / completed / failed
Background task.started / terminated, shell.started / output / completed
Misc compaction.*, skill.activated, goal.updated, prompt.*, error, warning

Three global lifecycle events keep a cross-workspace overview fresh without polling per workspace. event.session.archived fires on both the live and the cold archive path; its envelope session_id is the global watermark __global__ and the real session id rides in the payload: { "type": "event.session.archived", "workspace_id": "wd_...", "sessionId": "session_..." } (payload keys workspace_id / sessionId). event.workspace.created / updated carry the full workspace object ({ id, root, name, created_at, last_opened_at, session_count } — an updated also fires when a session creation touches the workspace), and event.workspace.deleted carries { "workspace_id", "root" }. These events only cover changes made inside this server process; changes from other processes (for example a CLI writing to the same home) surface through the index reconciliation (about a minute), so overview clients should keep a low-frequency fallback poll. There is no session-deleted event.

Events also split into durable and volatile: durable events carry a strictly increasing seq, are journaled, and can be replayed; volatile events (the *.delta family, tool.progress, shell.*, and similar) are marked volatile: true and never replayed. When consuming a volatile text stream, compare offset (the cumulative character offset within the turn) against your locally accumulated text: below the local length means a duplicate frame; above means a gap that needs snapshot recovery.

Reconnect and recovery

After reconnecting, pass each session's last applied {seq, epoch} in subscribe's cursors; the server replays the gap. If you fall more than the buffer (1000 events) behind, or the cursor is no longer valid, you get resync_required instead. In that case, call GET /api/v1/sessions/{session_id}/snapshot for a full snapshot (with as_of_seq and epoch), then subscribe again with the fresh cursor.

Transcript protocol

subscribe_v2's transcript field sets a per-agent grade: off / turn / block / delta (the "*" key sets the default grade), with higher grades pushing finer detail. An agent with a non-off grade receives two frame types: transcript.reset (a baseline snapshot; history pages in over REST) and transcript.ops (incremental op batches with a per-agent strictly increasing seq). The agent's legacy events are suppressed on that connection and carried by transcript frames instead. After a disconnect, resume with transcript_since; when the server's op journal cannot cover the gap (REST catch-up returns complete: false), do a full refresh. The REST counterparts are GET .../transcript (turn-paged) and GET .../transcript/ops?since_seq= (op-batch catch-up).

Binary and streaming endpoints

The following endpoints stream binary bodies instead of a JSON payload. Their HTTP capabilities differ per endpoint:

Method and path Description Range (206) ETag / 304
GET /api/v1/files/{file_id} Download an uploaded file Yes No (sends an etag header but ignores If-None-Match)
GET /api/v1/sessions/{session_id}/fs/{path}:download Download a session workspace file Yes Yes
GET /api/v1/fs:content Raw bytes of any host file (gated only by the token — be careful when exposing the port) Yes Yes
POST /api/v1/sessions/{session_id}/export Export the session with diagnostics (zip stream) No No

Error semantics differ as well: GET /api/v1/files/{file_id} answers lookup and storage failures with real 404 / 500 statuses (parameter validation still uses the HTTP 200 envelope), while the other three report every failure through the standard response envelope — clients must keep checking the envelope code on those endpoints.

Next steps