Skip to main content
Base URL https://api.opencomputer.dev/v3. Management calls authenticate with your org API key; stream and steer also accept a client token.

Sessions

Method · PathPurpose
POST /sessionsStart a session. Body: agent (required), input, key?, webhook?, destinations?[], limits?: { tokens?, turn_seconds?, turns? }. Returns { session, client_token }.
GET /sessions/:idFetch a session.
GET /sessions?status=&agent=&key=&after=&before=&limit=List sessions (filtered, paginated).
POST /sessions/:id/archiveCancel any active turn, then close (read-only).
POST /sessions/:id/cancelCooperatively stop the active turn (no-op if not running).
GET /sessions/:id/resultThe result helper — { last_turn, result? } (the resolved final event).
GET /sessions/:id/turns · GET …/turns/:turnIdPer-turn history: timing, usage, error.
POST /sessions/:id/client-tokensMint a client token. Body: scopes? (subset of read, steer), ttl? (seconds; clamped to 60–86400).
agent is required (create a saved agent first); a resolvable Anthropic credential is also required (422 no_credential otherwise). key gives get-or-create (one session per key); a request-level Idempotency-Key header makes a keyless create retry-safe. input is the task — a string or a full envelope (structured context rides input.refs: repo/branch/commit/issue/…). limits { tokens, turn_seconds, turns } are non-monetary — token budget, per-turn wall-clock, auto-run count; hitting one ends the turn with a matching last_turn.yield_reason. Destinations on an idempotent create-replay are ignored — manage them via the destinations API.
Session = { id, status, head, input_cursor,
            agent_snapshot: { prompt_hash, model, runtime, revision },
            agent_id, credential_id,   // resolved + pinned at create
            last_turn: { id, state, yield_reason?, result_event_id? },
            usage, limits: { tokens?, turn_seconds?, turns? }, key?, created_at }

Turn = { id, state, yield_reason?, started_at?, completed_at?,    // from GET …/result and …/turns
         result_event_id?, error?, active_seconds, usage }
yield_reason (completed | needs_input | error | budget_exceeded | deadline_exceeded | max_turns | canceled; requires_action is reserved, not emitted yet) is the machine-readable “why the turn ended” — needs_input means the agent asked a question; answer it by steering.

Events

Method · PathPurpose
GET /sessions/:id/events?after=<seq>&level=<lvl>&type=&turn_id=&limit=<n>Read events since a cursor (filter by type exact or prefix.*, or turn_id).
GET /sessions/:id/events?after=<seq>&level=<lvl>&stream=sseLive SSE stream from a cursor.
GET /sessions/:id/events/:eventIdFetch one event (resolve result_event_id).
GET /sessions/:id/events/:eventId/contentFetch a blob-backed body (large outputs).
GET /sessions/:id/messages?after=<seq>&limit=<n>User-message history (alias for level=user + type=*.message).
after resumes from a seq; level is a visibility thresholduser returns only user-facing events, progress adds work updates, internal adds everything (see levels). Omitting level defaults to internal (you get everything). Switch on type, don’t parse prose.
Event = { id, seq, ts, session, actor, type, level, body, refs, turn_id?,
          content_ref?, body_truncated?, body_bytes? }   // content_ref + body_* present when the body spilled to a blob

Steer

Method · PathPurpose
POST /sessions/:id/messagesAppend an input message; wakes the session. Body: { text, idempotency_key? } or { envelope }. 202 (or 200 on an idempotent replay) → { event: { id, seq }, session: { id, status, head } }.

Destinations

Method · PathPurpose
POST /sessions/:id/destinationsRegister a webhook. Body: url, secret?, level? (user), types?[] (event-type filter; exact or prefix.*), include_raw? (false), enabled? (true).
GET /sessions/:id/destinations · GET …/:didList / fetch.
PATCH /sessions/:id/destinations/:didPause/resume (enabled), retune level/types, rotate secret.
DELETE /sessions/:id/destinations/:didRemove.

Deliveries

Method · PathPurpose
GET /sessions/:id/deliveries?destination=&status=List deliveries — status, attempts, last response/error.
GET /sessions/:id/deliveries/:idOne delivery, in detail.
POST /sessions/:id/deliveries/:id/redeliverRe-send any delivery (not just dead-lettered).
See Webhooks for the signing and delivery contract.
Destination = { id, session, kind, url, level, types, include_raw, enabled,
                has_secret, created_after_seq, created_at, updated_at }   // secret never returned — has_secret signals it's set
Delivery    = { id, session, destination, event_id, event_seq, status, attempts,
                last_attempt_at, response_code?, error?, created_at, updated_at }

Agents

Method · PathPurpose
POST /agentsCreate a reusable agent. Body: name, prompt, model, runtime? (claude), key? (Anthropic key — creates + attaches a credential), credential? (reference an existing one), limits?: { tokens?, turn_seconds?, turns? }. Idempotent by (owner, name).
GET /agents/:id · GET /agentsFetch / list.
PATCH /agents/:idUpdate prompt? / model? / key? (rotate) / credential? / limits?. Never affects existing sessions (snapshots are pinned).
Pass key or credential (or neither → org default). The agent’s limits are the defaults for its sessions; a session’s own limits override. Each agent has a monotonic revision (bumped on PATCH) that sessions pin in agent_snapshot.revision, so you can tell which version produced a run.

Credentials

Method · PathPurpose
POST /credentialsAdd a provider key (Anthropic). Body: provider, key, name?, is_default?. Key is write-only. Required — sessions run on your key (no platform billing yet).
GET /credentials · DELETE /credentials/:idList / remove.
PUT /credentials/defaultSet the org default to a credential (its provider is derived). Body: credential.
Credential  = { id, provider, name?, last4, is_default, created_at }
ClientToken = { token, scopes, expires_at }

Errors & rate limits

Errors use one envelope: { "error": { "type", "message" } }. Cross-owner resources return 404 (not 403). Rate and concurrency limits are not enforced at launch (a 429 with a Retry-After header may be introduced later).