OpenCode exposes an OpenAPI 3.1-compliant HTTP API where the /session path group provides CRUD and action endpoints for managing sessions (create, list, fork, message, etc.), while /global and other path groups expose health checks, events, and ancillary services. The server is built on Effect as a composable web handler (OpenCodeHttpApi), shipped as both a lazily-initialized singleton (Server.Default) and a configurable listen function that returns a plain Listener object for stopping/force-closing connections.
The opencode server exposes an OpenAPI 3.1 spec at http://<hostname>:<port>/doc (e.g., http://localhost:4096/doc), which can be used to generate clients or viewed in a Swagger explorer.[1] Server.openapi() in packages/opencode/src/server/server.ts generates and returns the OpenAPI schema from PublicApi.[2] Server.Default in packages/opencode/src/server/server.ts is a lazily-initialized singleton that provides a fetch/request handler backed by HttpApiApp.webHandler(), suitable for in-process request dispatch without a real TCP listener.[2]
The OpenCodeHttpApi in packages/opencode/src/server/routes/instance/httpapi/api.ts is the root HTTP API that composes RootHttpApi, EventApi, InstanceHttpApi, ServerApi, and PtyConnectApi into a single Effect HttpApi.[3] RootHttpApi in packages/opencode/src/server/routes/instance/httpapi/api.ts applies SchemaErrorMiddleware and Authorization middleware to the ControlApi, ControlPlaneApi, and GlobalApi route groups.[3] OpenCodeHttpApi in packages/opencode/src/server/routes/instance/httpapi/api.ts registers additional schemas (EventSchema, Question.Replied, Question.Rejected, Credential.Value, Integration.Inputs, Integration.Method, Integration.Ref, SkillV2.Source) via HttpApi.AdditionalSchemas for OpenAPI generation.[3] The EventSchema in packages/opencode/src/server/routes/instance/httpapi/api.ts is a Schema.Union of all latest EventManifest event types plus InstanceDisposed, annotated with the identifier "Event".[3]
The Global API includes GET /global/health (returns { healthy: true, version: string }) and GET /global/event (an SSE stream of global events).[1] The /tui endpoint can drive the TUI programmatically — for example, to prefill or run a prompt — and this mechanism is used by OpenCode IDE plugins.[1]
packages/opencode/src/server/routes/instance/httpapi/groups/session.ts defines all session HTTP API endpoints under the /session path prefix, including: GET /session (list), GET /session/status, GET /session/:sessionID, GET /session/:sessionID/children, GET /session/:sessionID/todo, GET /session/:sessionID/diff, GET /session/:sessionID/message, GET /session/:sessionID/message/:messageID, POST /session (create), DELETE /session/:sessionID, PATCH /session/:sessionID (update), POST /session/:sessionID/fork, POST /session/:sessionID/abort, POST /session/:sessionID/share, POST /session/:sessionID/init, POST /session/:sessionID/summarize, POST /session/:sessionID/message (prompt), POST /session/:sessionID/prompt_async, POST /session/:sessionID/command, POST /session/:sessionID/shell, POST /session/:sessionID/revert, POST /session/:sessionID/unrevert, PATCH/DELETE /session/:sessionID/message/:messageID, and PATCH/DELETE /session/:sessionID/message/:messageID/part/:partID.[4] All session API endpoints in packages/opencode/src/server/routes/instance/httpapi/groups/session.ts require workspace routing query fields (via WorkspaceRoutingQuery or WorkspaceRoutingQueryFields) for multi-workspace targeting.[4]
The GET /session list endpoint in packages/opencode/src/server/routes/instance/httpapi/groups/session.ts accepts query params: scope (optional, "project"), path (optional string), roots (optional boolean), start (optional number), search (optional string), and limit (optional number), and returns sessions sorted by most recently updated.[4] The GET /session/:sessionID/message endpoint in packages/opencode/src/server/routes/instance/httpapi/groups/session.ts supports pagination via a limit (non-negative integer) and before (string cursor) query parameter, returning messages as SessionV1.WithParts[].[4] The GET /session/:sessionID/diff endpoint in packages/opencode/src/server/routes/instance/httpapi/groups/session.ts returns Snapshot.FileDiff[] representing file changes that resulted from a specific user message in the session.[4] The POST /session/:sessionID/message endpoint sends a message and waits for a response; its body accepts { messageID?, model?, agent?, noReply?, system?, tools?, parts }.[1] The PATCH /session/:sessionID update endpoint in packages/opencode/src/server/routes/instance/httpapi/groups/session.ts accepts optional fields: title (string), metadata (Session.Metadata), permission (PermissionV1.Ruleset), and time.archived (Session.ArchivedTimestamp).[4] DELETE /session/:sessionID in packages/opencode/src/server/routes/instance/httpapi/groups/session.ts permanently removes a session and all associated data including messages and history.[4] The POST /session/:sessionID/fork endpoint in packages/opencode/src/server/routes/instance/httpapi/groups/session.ts creates a new session by forking an existing session at a specific message point; its payload is derived from Session.ForkInput minus the sessionID (provided via path param).[4] The POST /session/:sessionID/init endpoint in packages/opencode/src/server/routes/instance/httpapi/groups/session.ts analyzes the app and creates AGENTS.md; its payload requires modelID, providerID, and messageID.[4][1] The POST /session/:sessionID/summarize endpoint in packages/opencode/src/server/routes/instance/httpapi/groups/session.ts accepts providerID, modelID, and an optional auto boolean flag.[4] The POST /session/:sessionID/permissions/:permissionID endpoint in packages/opencode/src/server/routes/instance/httpapi/groups/session.ts accepts a response field typed as PermissionV1.Reply, allowing the client to answer a pending permission request.[4]
The Server.listen function in packages/opencode/src/server/server.ts wraps the Effect-based listenEffect and returns a plain Listener object (with hostname, port, url, and stop) that callers outside the Effect runtime can use.[2] NodeHttpServer is configured with a gracefulShutdownTimeout of "1 second" in packages/opencode/src/server/server.ts.[2] listener.stop(true) in packages/opencode/src/server/server.ts force-closes all active HTTP connections and WebSocket connections concurrently before closing the listener scope; stop(false) or stop() performs a graceful shutdown without forcing connections.[2] forceClose in packages/opencode/src/server/server.ts calls both state.http.closeAll and state.websockets.closeAll concurrently with unbounded concurrency.[2] The serverLayer function in packages/opencode/src/server/server.ts monkey-patches server.close so that when forceStop is set (via ListenerServerService.closeAll), server.closeAllConnections() is called immediately upon server.close() invocation, allowing NodeHttpServer's own shutdown finalizer to honour the forced-close flag.[2] The mDNS unpublish effect in packages/opencode/src/server/server.ts is registered as a scope finalizer so it runs automatically when the listener's scope is closed.[2]
packages/opencode/src/server/server.ts suppresses AI SDK stdout warnings globally by setting globalThis.AI_SDK_LOG_WARNINGS = false before the server starts.[2]
Sources