app-server.md +143 −21
3Codex app-server is the interface Codex uses to power rich clients (for example, the Codex VS Code extension). Use it when you want a deep integration inside your own product: authentication, conversation history, approvals, and streamed agent events. The app-server implementation is open source in the Codex GitHub repository ([openai/codex/codex-rs/app-server](https://github.com/openai/codex/tree/main/codex-rs/app-server)). See the [Open Source](https://developers.openai.com/codex/open-source) page for the full list of open-source Codex components.3Codex app-server is the interface Codex uses to power rich clients (for example, the Codex VS Code extension). Use it when you want a deep integration inside your own product: authentication, conversation history, approvals, and streamed agent events. The app-server implementation is open source in the Codex GitHub repository ([openai/codex/codex-rs/app-server](https://github.com/openai/codex/tree/main/codex-rs/app-server)). See the [Open Source](https://developers.openai.com/codex/open-source) page for the full list of open-source Codex components.
4 4
5If you are automating jobs or running Codex in CI, use the5If you are automating jobs or running Codex in CI, use the
66[Codex SDK](https://developers.openai.com/codex/sdk) instead. <a href="/codex/sdk">Codex SDK</a> instead.
7 7
8## Protocol8## Protocol
9 9
12Supported transports:12Supported transports:
13 13
14- `stdio` (`--listen stdio://`, default): newline-delimited JSON (JSONL).14- `stdio` (`--listen stdio://`, default): newline-delimited JSON (JSONL).
1515- `websocket` (`--listen ws://IP:PORT`, experimental and unsupported): one JSON-RPC message per WebSocket text frame.- `websocket` (`--listen ws://IP:PORT`, experimental and unsupported): one
16 JSON-RPC message per WebSocket text frame.
17- Unix socket (`--listen unix://` or `--listen unix://PATH`): WebSocket
18 connections over Codex's default app-server control socket or a custom Unix
19 socket path, using the standard HTTP Upgrade handshake.
16- `off` (`--listen off`): don't expose a local transport.20- `off` (`--listen off`): don't expose a local transport.
17 21
1822When you run with `--listen ws://IP:PORT`, the same listener also serves basic HTTP health probes:When you run with `--listen ws://IP:PORT`, the same listener also serves basic
23HTTP health probes:
19 24
20- `GET /readyz` returns `200 OK` once the listener accepts new connections.25- `GET /readyz` returns `200 OK` once the listener accepts new connections.
2126- `GET /healthz` returns `200 OK` when the request doesn't include an `Origin` header.- `GET /healthz` returns `200 OK` when the request doesn't include an `Origin`
27 header.
22- Requests with an `Origin` header are rejected with `403 Forbidden`.28- Requests with an `Origin` header are rejected with `403 Forbidden`.
23 29
2430WebSocket transport is experimental and unsupported. Loopback listeners such as `ws://127.0.0.1:PORT` are appropriate for localhost and SSH port-forwarding workflows. Non-loopback WebSocket listeners currently allow unauthenticated connections by default during rollout, so configure WebSocket auth before exposing one remotely.WebSocket transport is experimental and unsupported. Local listeners such as
31`ws://127.0.0.1:PORT` are appropriate for localhost and SSH port-forwarding
32workflows. Non-loopback WebSocket listeners currently allow unauthenticated
33connections by default during rollout, so configure WebSocket auth before
34exposing one remotely.
25 35
26Supported WebSocket auth flags:36Supported WebSocket auth flags:
27 37
29- `--ws-auth capability-token --ws-token-sha256 HEX`39- `--ws-auth capability-token --ws-token-sha256 HEX`
30- `--ws-auth signed-bearer-token --ws-shared-secret-file /absolute/path`40- `--ws-auth signed-bearer-token --ws-shared-secret-file /absolute/path`
31 41
3242For signed bearer tokens, you can also set `--ws-issuer`, `--ws-audience`, and `--ws-max-clock-skew-seconds`. Clients present the credential as `Authorization: Bearer <token>` during the WebSocket handshake, and app-server enforces auth before JSON-RPC `initialize`.For signed bearer tokens, you can also set `--ws-issuer`, `--ws-audience`, and
43`--ws-max-clock-skew-seconds`. Clients present the credential as
44`Authorization: Bearer <token>` during the WebSocket handshake, and app-server
45enforces auth before JSON-RPC `initialize`.
33 46
3447Prefer `--ws-token-file` over passing raw bearer tokens on the command line. Use `--ws-token-sha256` only when the client keeps the raw high-entropy token in a separate local secret store; the hash is only a verifier, and clients still need the original token.Prefer `--ws-token-file` over passing raw bearer tokens on the command line. Use
48`--ws-token-sha256` only when the client keeps the raw high-entropy token in a
49separate local secret store; the hash is only a verifier, and clients still need
50the original token.
35 51
3652In WebSocket mode, app-server uses bounded queues. When request ingress is full, the server rejects new requests with JSON-RPC error code `-32001` and message `"Server overloaded; retry later."` Clients should retry with an exponentially increasing delay and jitter.In WebSocket mode, app-server uses bounded queues. When request ingress is full,
53the server rejects new requests with JSON-RPC error code `-32001` and message
54`"Server overloaded; retry later."` Clients should retry with an exponentially
55increasing delay and jitter.
37 56
38## Message schema57## Message schema
39 58
68 87
69## Getting started88## Getting started
70 89
71901. Start the server with `codex app-server` (default stdio transport) or `codex app-server --listen ws://127.0.0.1:4500` (experimental WebSocket transport).1. Start the server with `codex app-server` (default stdio transport),
91 `codex app-server --listen ws://127.0.0.1:4500` (TCP WebSocket), or
92 `codex app-server --listen unix://` (default Unix socket).
722. Connect a client over the selected transport, then send `initialize` followed by the `initialized` notification.932. Connect a client over the selected transport, then send `initialize` followed by the `initialized` notification.
733. Start a thread and a turn, then keep reading notifications from the active transport stream.943. Start a thread and a turn, then keep reading notifications from the active transport stream.
74 95
75Example (Node.js / TypeScript):96Example (Node.js / TypeScript):
76 97
77```ts98```ts
7899import { spawn } from "node:child_process";
79100import readline from "node:readline";
80 101
81const proc = spawn("codex", ["app-server"], {102const proc = spawn("codex", ["app-server"], {
82 stdio: ["pipe", "pipe", "inherit"],103 stdio: ["pipe", "pipe", "inherit"],
216 237
217- `thread/start` - create a new thread; emits `thread/started` and automatically subscribes you to turn/item events for that thread.238- `thread/start` - create a new thread; emits `thread/started` and automatically subscribes you to turn/item events for that thread.
218- `thread/resume` - reopen an existing thread by id so later `turn/start` calls append to it.239- `thread/resume` - reopen an existing thread by id so later `turn/start` calls append to it.
219240- `thread/fork` - fork a thread into a new thread id by copying stored history; emits `thread/started` for the new thread.- `thread/fork` - fork a thread into a new thread id by copying stored history; emits `thread/started` for the new thread. Returned threads include `forkedFromId` when available.
220- `thread/read` - read a stored thread by id without resuming it; set `includeTurns` to return full turn history. Returned `thread` objects include runtime `status`.241- `thread/read` - read a stored thread by id without resuming it; set `includeTurns` to return full turn history. Returned `thread` objects include runtime `status`.
221- `thread/list` - page through stored thread logs; supports cursor-based pagination plus `modelProviders`, `sourceKinds`, `archived`, `cwd`, and `searchTerm` filters. Returned `thread` objects include runtime `status`.242- `thread/list` - page through stored thread logs; supports cursor-based pagination plus `modelProviders`, `sourceKinds`, `archived`, `cwd`, and `searchTerm` filters. Returned `thread` objects include runtime `status`.
222243- `thread/turns/list` - page through a stored thread's turn history without resuming it.- `thread/turns/list` - page through a stored thread's turn history without resuming it. `itemsView` controls whether turn items are omitted, summarized, or fully loaded.
244- `thread/turns/items/list` - reserved for paged turn-item loading; currently returns unsupported.
223- `thread/loaded/list` - list the thread ids currently loaded in memory.245- `thread/loaded/list` - list the thread ids currently loaded in memory.
224- `thread/name/set` - set or update a thread's user-facing name for a loaded thread or a persisted rollout; emits `thread/name/updated`.246- `thread/name/set` - set or update a thread's user-facing name for a loaded thread or a persisted rollout; emits `thread/name/updated`.
225- `thread/goal/set` - set the goal for a loaded thread (experimental; requires `capabilities.experimentalApi`); emits `thread/goal/updated`.247- `thread/goal/set` - set the goal for a loaded thread (experimental; requires `capabilities.experimentalApi`); emits `thread/goal/updated`.
244- `command/exec/resize` - resize a running PTY-backed `command/exec` session.266- `command/exec/resize` - resize a running PTY-backed `command/exec` session.
245- `command/exec/terminate` - stop a running `command/exec` session.267- `command/exec/terminate` - stop a running `command/exec` session.
246- `command/exec/outputDelta` (notify) - emitted for base64-encoded stdout/stderr chunks from a streaming `command/exec` session.268- `command/exec/outputDelta` (notify) - emitted for base64-encoded stdout/stderr chunks from a streaming `command/exec` session.
269- `process/spawn` - start an explicit process session outside Codex's sandbox (experimental; requires `capabilities.experimentalApi`).
270- `process/writeStdin` - write stdin bytes to a running `process/spawn` session or close stdin (experimental).
271- `process/resizePty` - resize a running PTY-backed process session (experimental).
272- `process/kill` - terminate a running process session (experimental).
273- `process/outputDelta` and `process/exited` (notify) - emitted for streaming process output and process exit status (experimental).
247- `model/list` - list available models (set `includeHidden: true` to include entries with `hidden: true`) with effort options, optional `upgrade`, and `inputModalities`.274- `model/list` - list available models (set `includeHidden: true` to include entries with `hidden: true`) with effort options, optional `upgrade`, and `inputModalities`.
248- `modelProvider/capabilities/read` - read provider capability bounds for model/provider combinations (experimental; requires `capabilities.experimentalApi`).275- `modelProvider/capabilities/read` - read provider capability bounds for model/provider combinations (experimental; requires `capabilities.experimentalApi`).
249- `experimentalFeature/list` - list feature flags with lifecycle stage metadata and cursor pagination.276- `experimentalFeature/list` - list feature flags with lifecycle stage metadata and cursor pagination.
250277- `experimentalFeature/enablement/set` - patch in-memory runtime enablement for supported feature keys such as `apps` and `plugins`.- `experimentalFeature/enablement/set` - patch in-memory runtime settings for supported feature keys such as `apps` and `plugins`.
251- `collaborationMode/list` - list collaboration mode presets (experimental, no pagination).278- `collaborationMode/list` - list collaboration mode presets (experimental, no pagination).
252- `skills/list` - list skills for one or more `cwd` values (supports `forceReload` and optional `perCwdExtraUserRoots`).279- `skills/list` - list skills for one or more `cwd` values (supports `forceReload` and optional `perCwdExtraUserRoots`).
253- `skills/changed` (notify) - emitted when watched local skill files change.280- `skills/changed` (notify) - emitted when watched local skill files change.
351## Threads378## Threads
352 379
353- `thread/read` reads a stored thread without subscribing to it; set `includeTurns` to include turns.380- `thread/read` reads a stored thread without subscribing to it; set `includeTurns` to include turns.
354381- `thread/turns/list` pages through a stored thread's turn history without resuming it.- `thread/turns/list` pages through a stored thread's turn history without
382 resuming it. Use `itemsView` to choose whether turn items are omitted,
383 summarized, or fully loaded.
355- `thread/list` supports cursor pagination plus `modelProviders`, `sourceKinds`, `archived`, `cwd`, and `searchTerm` filtering.384- `thread/list` supports cursor pagination plus `modelProviders`, `sourceKinds`, `archived`, `cwd`, and `searchTerm` filtering.
356- `thread/loaded/list` returns the thread IDs currently in memory.385- `thread/loaded/list` returns the thread IDs currently in memory.
357- `thread/archive` moves the thread's persisted JSONL log into the archived directory.386- `thread/archive` moves the thread's persisted JSONL log into the archived directory.
378{ "id": 10, "result": {407{ "id": 10, "result": {
379 "thread": {408 "thread": {
380 "id": "thr_123",409 "id": "thr_123",
410 "sessionId": "thr_123",
381 "preview": "",411 "preview": "",
382 "ephemeral": false,412 "ephemeral": false,
383 "modelProvider": "openai",413 "modelProvider": "openai",
389 419
390`serviceName` is optional. Set it when you want app-server to tag thread-level metrics with your integration's service name.420`serviceName` is optional. Set it when you want app-server to tag thread-level metrics with your integration's service name.
391 421
422`thread.sessionId` identifies the current live session tree root. Root threads
423use their own thread id as the session id; forked threads keep the session id
424of the root they came from. Clients should read the session id from
425`thread.sessionId` instead of deriving it from the thread id.
426
392To continue a stored session, call `thread/resume` with the `thread.id` you recorded earlier. The response shape matches `thread/start`. You can also pass the same configuration overrides supported by `thread/start`, such as `personality`:427To continue a stored session, call `thread/resume` with the `thread.id` you recorded earlier. The response shape matches `thread/start`. You can also pass the same configuration overrides supported by `thread/start`, such as `personality`:
393 428
394```json429```json
407 442
408If you resume with a different model than the one recorded in the rollout, Codex emits a warning and applies a one-time model-switch instruction on the next turn.443If you resume with a different model than the one recorded in the rollout, Codex emits a warning and applies a one-time model-switch instruction on the next turn.
409 444
445### Manage a thread goal
446
447`thread/goal/set`, `thread/goal/get`, and `thread/goal/clear` are experimental
448and require `capabilities.experimentalApi = true` plus the `goals` feature. Use
449them for the same persisted goal state surfaced by `/goal` in the TUI.
450
451```json
452{ "method": "thread/goal/set", "id": 13, "params": {
453 "threadId": "thr_123",
454 "objective": "Finish the migration and keep tests green",
455 "status": "active",
456 "tokenBudget": 40000
457} }
458{ "id": 13, "result": { "goal": {
459 "threadId": "thr_123",
460 "objective": "Finish the migration and keep tests green",
461 "status": "active",
462 "tokenBudget": 40000,
463 "tokensUsed": 0,
464 "timeUsedSeconds": 0
465} } }
466{ "method": "thread/goal/updated", "params": {
467 "threadId": "thr_123",
468 "goal": {
469 "threadId": "thr_123",
470 "objective": "Finish the migration and keep tests green",
471 "status": "active",
472 "tokenBudget": 40000,
473 "tokensUsed": 0,
474 "timeUsedSeconds": 0
475 }
476} }
477```
478
479Goal objectives must be non-empty and at most 4,000 characters. Supplying a new
480objective replaces the goal and resets usage accounting. Supplying the current
481non-terminal objective, or omitting `objective`, updates status or token budget
482while preserving usage history.
483
410To branch from a stored session, call `thread/fork` with the `thread.id`. This creates a new thread id and emits a `thread/started` notification for it:484To branch from a stored session, call `thread/fork` with the `thread.id`. This creates a new thread id and emits a `thread/started` notification for it:
411 485
412```json486```json
413{ "method": "thread/fork", "id": 12, "params": { "threadId": "thr_123" } }487{ "method": "thread/fork", "id": 12, "params": { "threadId": "thr_123" } }
414488{ "id": 12, "result": { "thread": { "id": "thr_456" } } }{ "id": 12, "result": { "thread": { "id": "thr_456", "sessionId": "thr_123", "forkedFromId": "thr_123" } } }
415{ "method": "thread/started", "params": { "thread": { "id": "thr_456" } } }489{ "method": "thread/started", "params": { "thread": { "id": "thr_456" } } }
416```490```
417 491
435 509
436Use `thread/turns/list` to page a stored thread's turn history without resuming it. Results default to newest-first so clients can fetch older turns with `nextCursor`. The response also includes `backwardsCursor`; pass it as `cursor` with `sortDirection: "asc"` to fetch turns newer than the first item from the earlier page.510Use `thread/turns/list` to page a stored thread's turn history without resuming it. Results default to newest-first so clients can fetch older turns with `nextCursor`. The response also includes `backwardsCursor`; pass it as `cursor` with `sortDirection: "asc"` to fetch turns newer than the first item from the earlier page.
437 511
512`itemsView` controls how much turn-item data the response includes:
513
514- `notLoaded` omits items.
515- `summary` returns summarized item data and is the default when omitted.
516- `full` returns full item data.
517
438```json518```json
439{ "method": "thread/turns/list", "id": 20, "params": {519{ "method": "thread/turns/list", "id": 20, "params": {
440 "threadId": "thr_123",520 "threadId": "thr_123",
441 "limit": 50,521 "limit": 50,
442522 "sortDirection": "desc" "sortDirection": "desc",
523 "itemsView": "summary"
443} }524} }
444{ "id": 20, "result": {525{ "id": 20, "result": {
445 "data": [],526 "data": [],
448} }529} }
449```530```
450 531
532`thread/turns/items/list` is reserved for paged turn-item loading, but the
533current server returns an unsupported-method error.
534
451### List threads (with pagination & filters)535### List threads (with pagination & filters)
452 536
453`thread/list` lets you render a history UI. Results default to newest-first by `createdAt`. Filters apply before pagination. Pass any combination of:537`thread/list` lets you render a history UI. Results default to newest-first by `createdAt`. Filters apply before pagination. Pass any combination of:
825 909
826Use this notification to render the reviewer output in your client.910Use this notification to render the reviewer output in your client.
827 911
912## Process execution
913
914`process/*` is an experimental, explicit process-control API. It requires
915`capabilities.experimentalApi = true` and runs outside Codex's sandbox. Use it
916only when your client intentionally exposes local process control without a
917sandbox.
918
919Start a process with `process/spawn` and provide a `processHandle`, then use
920that handle for stdin, resize, and kill requests. Output streams through
921`process/outputDelta` notifications and completion streams through
922`process/exited`.
923
924```json
925{ "method": "process/spawn", "id": 48, "params": {
926 "command": ["python3", "-m", "pytest", "-q"],
927 "processHandle": "pytest-1",
928 "cwd": "/Users/me/project",
929 "tty": true
930} }
931{ "id": 48, "result": {} }
932{ "method": "process/outputDelta", "params": {
933 "processHandle": "pytest-1",
934 "stream": "stdout",
935 "deltaBase64": "Li4u"
936} }
937{ "method": "process/exited", "params": {
938 "processHandle": "pytest-1",
939 "exitCode": 0
940} }
941```
942
943Use `process/writeStdin` with `deltaBase64`, `closeStdin`, or both to send
944input. Use `process/resizePty` for PTY resize events and `process/kill` to
945terminate a running process.
946
828## Command execution947## Command execution
829 948
830`command/exec` runs a single command (`argv` array) under the server sandbox without creating a thread.949`command/exec` runs a single command (`argv` array) under the server sandbox without creating a thread.
990- `item/reasoning/summaryPartAdded` - marks a boundary between reasoning summary sections.1109- `item/reasoning/summaryPartAdded` - marks a boundary between reasoning summary sections.
991- `item/reasoning/textDelta` - streams raw reasoning text (when supported by the model).1110- `item/reasoning/textDelta` - streams raw reasoning text (when supported by the model).
992- `item/commandExecution/outputDelta` - streams stdout/stderr for a command; append deltas in order.1111- `item/commandExecution/outputDelta` - streams stdout/stderr for a command; append deltas in order.
9931112- `item/fileChange/outputDelta` - contains the tool call response of the underlying `apply_patch` tool call.- `item/fileChange/outputDelta` - deprecated compatibility notification for legacy `apply_patch` text output. Current app-server versions no longer emit it; use `fileChange` items and `turn/diff/updated` instead.
994 1113
995## Errors1114## Errors
996 1115
1049 1169
1050`dynamicTools` on `thread/start` and the corresponding `item/tool/call` request or response flow are experimental APIs.1170`dynamicTools` on `thread/start` and the corresponding `item/tool/call` request or response flow are experimental APIs.
1051 1171
1172Dynamic tool names and namespace names must follow Responses API naming
1173constraints. Avoid reserved namespace names used by built-in Codex tools.
1174
1052When a dynamic tool is invoked during a turn, app-server emits:1175When a dynamic tool is invoked during a turn, app-server emits:
1053 1176
10541. `item/started` with `item.type = "dynamicToolCall"`, `status = "inProgress"`, plus `tool` and `arguments`.11771. `item/started` with `item.type = "dynamicToolCall"`, `status = "inProgress"`, plus `tool` and `arguments`.
1655- `usedPercent` is current usage within the quota window.1784- `usedPercent` is current usage within the quota window.
1656- `windowDurationMins` is the quota window length.1785- `windowDurationMins` is the quota window length.
1657- `resetsAt` is a Unix timestamp (seconds) for the next reset.1786- `resetsAt` is a Unix timestamp (seconds) for the next reset.
16581787- `planType` is included when the backend returns the ChatGPT plan associated with a bucket.- `planType` is included when the server returns the ChatGPT plan associated with a bucket.
16591788- `credits` is included when the backend returns remaining workspace credit details.- `credits` is included when the server returns remaining workspace credit details.
16601789- `rateLimitReachedType` identifies the backend-classified limit state when one has been reached.- `rateLimitReachedType` identifies the server-classified limit state when one has been reached.
1661 1790
1662### 7) Notify a workspace owner about a limit1791### 7) Notify a workspace owner about a limit
1663 1792