Skip to main content
The management API is the HTTP surface behind the CLI, the dashboard, the SDK helpers and the React hook’s proxy routes. Use it from trusted server code to run agents from your own application.

Base URL and authentication

Every request carries an API key in the x-api-key header. A key belongs to an organization and reaches every project, agent and session in it. Keep it on the server; the React integration shows how a browser attaches through your own routes without seeing the key. Your server must authenticate callers and enforce which projects, environments, agents and sessions they may access. A list filter or session label is not an authorization boundary. Request and response bodies are JSON. Paths below are relative to the base URL; <p> is a project ID and <r> a memory resource ID. The TypeScript client on @opencomputer/sdk/agents mirrors these routes one to one.

Errors

Error bodies are { error: { code, message } }. The code is stable and is what to branch on; the message is short and safe to show. A missing key and an unknown route return { "error": "<text>" } without a code.

Sessions

A session is a durable conversation with one deployed agent. It keeps the deployment it was created on; see Sessions and turns.

Create

The response is { session: { id, executionMode, status, createdAt, externalReference? }, deployment }. The session starts without a turn; send one with the turns route. Idempotency-Key (at most 256 characters) makes a retry safe. The key identifies the session within your organization:
  • The same key with the same request, meaning the same agentId (or the same pinned deploymentId), environment and memory bindings, returns the existing session with 200, together with the deployment it pinned when it was first created. That holds after a redeploy or a rollback: the session keeps the deployment it started with, and your retry does not need to know which one that was. A new session under a new key gets the deployment active at that time.
  • Anything else under that key is 409 idempotency_conflict: another agent, environment or pinned deployment, different memory bindings, or a different externalReference (including adding or dropping one). Pinning the deployment the session already holds counts as the same request.
  • Two identical requests under one key that arrive together create one session; both answers name it, one with 201 and the other with 200.
  • Sessions created before this rule was recorded keep the earlier one: a replay through an alias succeeds only while the alias still resolves to the deployment the session pinned.
  • 503 memory_admission_unconfirmed means the memory grants were not confirmed in time. Retry with the same key; the retry resumes the wait.
  • 503 session_publication_unconfirmed means the session exists but its row in the list was not confirmed in time; error.sessionId names it. Retry with the same key and the same body until the answer is 201 or 200. A success means GET /sessions reflects the session; the platform keeps publishing the row in the background either way.
Other codes: 400 invalid_environment, 400 invalid_memory_binding, 400 invalid_labels, 400 invalid_external_reference, 404 deployment_not_found, 409 deployment_not_promoted (a pinned deployment is not active in the named environment), 402 insufficient_credits.

Get and list

GET /sessions/<id> returns the session: GET /sessions returns one page of rows, { sessions, nextCursor }, filtered by the query:
Every filter is an exact match; there is no prefix, substring or wildcard search. Filters combine with AND. Naming both spellings of a parameter (project and projectId, agent and agentId), any other parameter or a malformed value is 400 invalid_query. Filters select among the sessions the API key can already see: an organization key sees the organization’s sessions, a project-scoped key only its project’s, and a filter naming another project is 403 project_scope_violation. A development filter never returns Production sessions, and a reference created in one project is not found from another. A row is id, projectId, agentId, deploymentId, environment (null when the session has none), source, status, labels, externalReference (when one was given), createdAt, updatedAt, revision, activity and result. activity is { activeTurnId, queued, lastSettledTurn }: the running turn’s id or null, how many turns wait behind it, and the most recently finished turn as { id, status, at } or null. Rows carry no turns, no memory and no prompt or message text; read GET /sessions/<id> for those. Rows are ordered by createdAt, newest first, then by id, so sessions created in the same millisecond have a fixed order and none is skipped. Updates do not move rows. nextCursor is an opaque string, valid only with the filters of the page that returned it: sending it with different filters is 400 invalid_cursor, and so is a cursor that cannot be read. limit may change between pages. null means there are no more matches after that cursor. Pagination is not a snapshot: each request applies the filters to the current rows. New sessions and sessions that newly match a status or label filter can fall before your cursor. Refresh from the first page to see them. Sorting fetched rows by updatedAt orders only those rows, not all matching sessions. A successful create response confirms the session is listed.

External references

externalReference is one opaque string your application chooses when it creates a session, for example the id of the order, ticket or job the session works for. It is 1 to 256 bytes of UTF-8 with no control characters; anything else is 400 invalid_external_reference. Omitting it, or sending null, creates a session without one; it cannot be changed afterwards, and it is not required to be unique. The platform stores the reference as given. It is not sent to the agent or the model, does not grant access, and does not replace the session id or the Idempotency-Key. Keep your own mapping from reference to session id as the authoritative record; the reference is there for recovery and audit when that record is incomplete:
  • If a create response was lost, retry with the same Idempotency-Key first; it returns the same session with 200. The reference makes a second recovery path available: GET /sessions?externalReference=<ref> finds the session however many unrelated sessions were created since.
  • A retry of the key with a different reference is 409 idempotency_conflict, so two records can never share one session by accident.
  • The reference is returned on the session, on its list row and in the data of every session.* event, so a consumer of the event log can route each event to the record it belongs to without another lookup.
A reference is metadata, not a credential: do not put secrets, tokens or signed URLs in it. It is visible to every API key that can read the session.

Labels

Labels are your own metadata on a session: at most 16 keys, each matching ^[a-z][a-z0-9_.-]{0,63}$, each value at most 256 characters, 4 KB in all. They filter the list and appear on the session and its row. They are not permissions, are not searched beyond equality, and are never shown to the agent; give the agent its context in the turn input. PATCH /sessions/<id>/labels changes them and returns the session:
unset removes keys, then set writes keys; each key takes the last write. The session’s labelsUpdatedAt records the change. A patch that would exceed the bounds, or a key or value that does not fit them, is 400 invalid_labels and changes nothing. Labels can be changed on an ended session. A 200 means the list reflects the change: the session’s row carries the new labels and the filters see them. 503 session_publication_unconfirmed (with error.sessionId) means the labels are recorded on the session but the row was not confirmed in time; retry the same patch until it answers 200. The platform keeps publishing the row in the background either way, and repeating the patch is safe because each key takes the last write.

End and interrupt

POST /sessions/<id>/end ends the session. Queued and running turns are marked cancelled with reason session_ended, and no further turns are accepted. A 200 confirms memory write access is revoked. 503 session_end_unconfirmed means the session ended but memory revocation was not acknowledged; retry the same end request, or freeze the document. Repeating end confirms any pending memory revocation without ending the session again. The session.ended event is recorded before memory revocation is confirmed. Neither that event nor the end response waits for commands to stop; remote cleanup continues in the background. Interrupt provides command-settlement confirmation for the current turn, subject to the compatibility note below; it leaves queued turns available to run. POST /sessions/<id>/interrupt stops the running turn without spending a model turn. The response acknowledges the stop request; it does not wait for completion. While stopping, the session is stopping and the turn remains running. Once the model loop and its commands have stopped, the turn becomes cancelled with reason interrupted, the session becomes idle, and the next queued turn can start. If a command cannot be confirmed stopped, OpenComputer terminates its computer before settling the turn. The next command uses a fresh computer with the session’s workspace. The cancellation event records the settlement details. An idle session is returned unchanged. Stopping does not undo files already written or external effects.
Sessions created with executionMode: "microvm" cancel immediately on interrupt, without waiting for their commands to stop. They do not enter stopping, and their cancellation events have no settlement fields. Check the creation response when this guarantee matters to your app.

Turns

Idempotency-Key is the one rule on both routes: it identifies this request, a repeat returns the existing turn, and a different request under the same key is refused (below). Without one every request starts a turn. The body field idempotencyKey carries the same key for a send that a browser makes through your own server, where the header is the server’s to set; when both are present they must be equal, else 400 invalid_turn. The response is 202 { turnId, status, duplicate } for a new turn and 200 with duplicate: true when the key had already created one. status is the turn’s status: queued or running for a new turn, and for a repeated key whatever the existing turn has reached, completed, failed or cancelled once it has settled. Follow the turn in the event log; the response does not wait for it. The key identifies the request, not only the call. A repeat with the same input, payload and mode is the existing turn: key order inside the payload does not matter, an omitted mode means queue, and whitespace in the text counts. The same key with anything else is 409 idempotency_conflict and admits nothing. A retry after a lost reply is therefore safe with the same body, and a different request never lands on another request’s key. Codes: 400 invalid_turn, 400 invalid_payload (over 32 KB), 402 insufficient_credits, 409 idempotency_conflict, 409 question_stale, 409 memory_admission_pending (retry the session creation with its key first), 409 memory_admission_rejected (the session is ended; create a new one).

Questions

While the session has an open question, a turn sent without answers is held rather than run: the response is 202 { status: "held", questionId, duplicate } with no turnId, and 200 for a repeated key. The held input is delivered with the answer, in order, as the answering turn’s steering, up to a bounded number and 64 KiB; the rest run as ordinary turns after it. A repeat of its key after delivery returns the receipt of the turn that carried it; after a Linear stop, it returns status: "discarded". A turn already queued when the question was asked keeps its turnId and settles as cancelled with reason held and the questionId. A turn with answers resolves the question and runs as the answer, in one step, idempotent on its key; the same key with a different answers is 409 idempotency_conflict. Naming a question that is not the open one, or answering when none is open, is 409 question_stale; read the session and answer its current question. POST /sessions/<id>/questions/<questionId>/dismiss closes the open question with reason dismissed; its held inputs run as ordinary turns, in order. Ending the session closes it with ended. Interrupt does not close it.

Start work from an application

An application that turns a form submission into agent work makes two calls, each with its own key, and keeps what it sent until the second one is acknowledged:
  1. POST /sessions with Idempotency-Key: <submission-id> and agentId: <agent-id>@<environment>. The platform chooses the deployment active in that environment and records it on the session; a retry under the same key returns that session, even if the alias has moved since. The application never discovers, verifies or stores a deployment id.
  2. POST /sessions/<id>/turns with Idempotency-Key: <submission-id>/start, the text and the payload.
Keep the submission (its id, the text and the payload) until the turns call answers, with duplicate: false or true. A lost reply at either step is retried with the same body and the same key: the first step returns the same session, the second the same turn. A session that exists without a turn is work that was never admitted; only the holder of the submission can retry it. A 409 idempotency_conflict from either call means a different request already used that id; show it, keep the draft, and do not append the submission to a session it did not create. Each later message is its own submission with its own key.

Events

GET /sessions/<id>/events?after=<seq> returns { events }: up to 500 events with seq greater than after, in ascending order. Each event is { id, seq, timestamp, sessionId, turnId, type, data }; turnId is absent on session-level events. To read a whole log, start at after=0 and repeat with the last seq you received until a page is empty. A full page means more may follow; read on without waiting. To follow a live session, keep polling from the last seq; the CLI’s sessions tail and useAgent do exactly this. The log is durable, so a consumer that stops can resume from its cursor and miss nothing. Event types and their data are listed on Session events.

Memory

Document memory belongs to a project and an environment. Every route takes ?environment=development or ?environment=production. Bodies, conditional headers, the document object and the error codes are on Document memory.
oc.sessions.startOnDocument on the TypeScript client does this create and the session create in one call.

Webhooks

Webhook configuration lives under the project; invocation uses the separate /api/agent-webhooks/<id>/<token> URL described on Agent webhooks. identity is header:<name> or body:<json-pointer>. Creating a webhook for an agent that is not deployed in the environment is 409 webhook_target_unavailable.

Event subscriptions

An event subscription delivers the recorded outcome of turns run by agents in a project to a session in the same project, as a new turn of that session. It is how one agent learns that another finished: a coordinator subscribes to its workers’ outcomes and reasons about them when they arrive. Destinations are sessions only; there is no public HTTPS destination, and this is not the outbound webhooks feature.
A subscription is immutable; { subscription } carries id, projectId, the fields above and createdAt. Codes: 400 invalid_event_subscription, 403 project_scope_violation (the agent is outside the project), 404 destination_session_not_found, 409 destination_session_ended, 404 event_subscription_not_found.

Delivery and receipts

When a selected turn settles, the source session records one delivery per matching subscription and starts a turn on the destination session with source: "event" input; the receiving agent reads it with useInput() (Event input). The destination turn’s idempotency key is <subscription-id>:<event-id>, so a retried delivery never starts a second turn. Deliveries are retried with backoff and stop when the subscription is deleted or the destination has ended. GET /sessions/<id> on the source session lists each turn’s deliveries: The delivered event names identifiers and the outcome; a completed turn’s final message is included, bounded to 16 KB. The receiving agent should treat that text as data about another agent’s work, not as instructions.

Linear connections

A Linear connection binds a Linear app in your workspace to one agent in one environment. The dashboard drives these routes. A project-scoped key may list its own project’s connections; every other route needs an organization API key and is 403 forbidden otherwise. createAppUrl opens Linear’s create-app page prefilled with the name, the callback URL and webhookUrl. Creating again for a pending binding resets it with the new name and a new webhookUrl, so an earlier createAppUrl stops working. webhookUrl contains the connection’s secret; treat it as a credential. authorizeUrl is Linear’s consent page for the app; a workspace admin completes it, and Linear returns to OpenComputer’s callback, which stores the tokens and redirects to the project’s Connections tab. revoked on a disconnect says whether Linear confirmed the token revocation. A connection is { id, projectId, environment, agentId, name, status, clientId, appUserId, organizationId, webhookUrl, createAppUrl, verifiedAt, verificationError, lastEventAt, teams, health, revision, createdAt, updatedAt }. It never carries a secret or a token. status is pending, connected, disconnected or revoked. health is { state, message, lastEventAt? }, with state one of awaiting_credentials, awaiting_authorization, waiting_for_first_delegation, receiving or revoked. teams is { allPublic, teamIds }, the teams the app can see.

Projects, agents and deployments

A project is { id, slug, name, environmentMode, environments, agents, createdAt, updatedAt }; agents carries each agent’s id and name. Single-environment projects have one environment named default; requests that name development resolve to it, and requests that name production fail with 409 single_environment_project. See Projects and agents for how projects are created and linked from the CLI.
Secrets, runtime variables, schedules and logs are managed with the CLI and the dashboard; see their pages under Concepts.