Management API
Create sessions, send turns, read the event log and manage projects over HTTP
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
https://app.opencomputer.dev/api/managed-agentsEvery 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.
| Status | Meaning |
|---|---|
400 | Invalid body, query or header |
401 | Missing or invalid API key |
402 | insufficient_credits: the organization is out of minutes or credits; error.haltReason says why and error.actionUrl links Billing |
403 | The key may not perform this operation |
404 | The route or the target does not exist |
409 | The request conflicts with current state, for example a reused Idempotency-Key |
429 | Too many requests; retry after Retry-After |
5xx | Temporary failure; retry with the same idempotency key |
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.
| Operation | Method and path | Success |
|---|---|---|
| Create | POST /sessions | 201 with the session, 200 when the key had already created it |
| Get | GET /sessions/<id> | 200 with the session |
| List | GET /sessions | 200 with { sessions, nextCursor }: one page of rows |
| Labels | PATCH /sessions/<id>/labels | 200 with the session |
| End | POST /sessions/<id>/end | 200 with the ended session |
| Interrupt | POST /sessions/<id>/interrupt | 200 with the session |
| Dismiss a question | POST /sessions/<id>/questions/<questionId>/dismiss | 200; see Questions |
Create
curl -X POST 'https://app.opencomputer.dev/api/managed-agents/sessions' \
-H 'x-api-key: <api-key>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: topic/workshop/1' \
-d '{
"agentId": "<agent-id>@development",
"labels": { "topic": "workshop", "requested_by": "u-42" },
"memory": {
"notes": { "scope": "document", "id": "workshop", "access": "read-write" }
}
}'| Field | Meaning |
|---|---|
agentId | <agent-id>@development or <agent-id>@production. The alias selects the environment and the deployment active in it. A bare agent ID means production. This is how applications address an agent; which deployment runs the session is the platform’s choice, recorded on the session. |
deploymentId | Advanced: pin one deployment instead of resolving an alias, for reproducibility or testing. Send environment with it; it may be omitted only when the deployment is promoted to exactly one environment. |
environment | development or production for legacy projects; default for single-environment projects. Optional with agentId; it must agree with the alias. |
memory | Bindings keyed by resource ID, at most eight. Needs a deployed agent and an environment. Shapes on Document memory. |
source | api (default), playground or webhook. The dashboard groups sessions by it. |
labels | Your own metadata on the session, an object of strings; see Labels. Applied at creation and ignored on a replay of the same Idempotency-Key. |
externalReference | Your own reference for the session, such as the id of the record it works for: a string of 1 to 256 bytes of UTF-8 without control characters. Stored as given and never interpreted; it is not shown to the agent, grants nothing and does not replace the session id or the Idempotency-Key. Returned on the session, its list row and every session.* event; see External references. |
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 pinneddeploymentId), environment and memory bindings, returns the existing session with200, 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 differentexternalReference(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
201and the other with200. - 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_unconfirmedmeans the memory grants were not confirmed in time. Retry with the same key; the retry resumes the wait.503 session_publication_unconfirmedmeans the session exists but its row in the list was not confirmed in time;error.sessionIdnames it. Retry with the same key and the same body until the answer is201or200. A success meansGET /sessionsreflects 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:
| Field | Meaning |
|---|---|
id, agentId, deploymentId | The session and the deployment it runs on, chosen when it was created |
projectId | The project the agent belongs to |
environment | development or production, when the request named one |
status | new, connecting, idle, running, stopping, waiting_runtime, suspending, suspended, resuming, failed or ended |
source | The source given at creation |
labels, labelsUpdatedAt | Your metadata, and when it last changed; see Labels |
externalReference | The reference given at creation; absent when none was given |
memory | Bindings with resource, scope, id, access and writable; present when the session was created with bindings |
turns | Every turn: id, input, mode, status, createdAt, updatedAt; outcome: "question" on a completed turn that ended by asking; and deliveries when an event subscription selected its outcome |
result | The latest committed output of the agent’s result tool, or null: { turnId, callId, reportedAt, data }. turnId and callId name the call that reported data, so a reader can tell which turn it came from and whether a later turn changed things without reporting. A failed or cancelled turn never clears it. |
question | The open question the agent asked with ask, or null: { id, text, options, askedAt }, options an array of { label, value }, empty for free text. Independent of result. |
revision | A counter that increases with every change to the session’s status, turns, labels or result |
createdAt, updatedAt | Timestamps |
GET /sessions returns one page of rows, { sessions, nextCursor }, filtered
by the query:
curl 'https://app.opencomputer.dev/api/managed-agents/sessions?project=<project-id>&environment=development&label.topic=workshop&limit=50' \
-H 'x-api-key: <api-key>'| Parameter | Meaning |
|---|---|
project or projectId | A project id or slug |
environment | development or production |
agent or agentId | An agent id |
status | One session status |
deploymentId | A deployment id |
externalReference | Sessions created with exactly this external reference |
createdAfter, createdBefore | RFC 3339 timestamps: sessions with createdAt >= createdAfter and createdAt < createdBefore |
updatedAfter | An RFC 3339 timestamp: sessions with updatedAt >= updatedAfter |
label.<key>=<value> | Sessions whose label <key> equals <value> exactly; at most three |
limit | Rows per page, 1 to 100; default 50 |
cursor | The nextCursor of the previous page |
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-Keyfirst; it returns the same session with200. 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
dataof everysession.*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:
curl -X PATCH 'https://app.opencomputer.dev/api/managed-agents/sessions/<session-id>/labels' \
-H 'x-api-key: <api-key>' \
-H 'Content-Type: application/json' \
-d '{ "set": { "outcome": "merged" }, "unset": ["requested_by"] }'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
curl -X POST 'https://app.opencomputer.dev/api/managed-agents/sessions/<session-id>/turns' \
-H 'x-api-key: <api-key>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: message-42' \
-d '{
"input": "Plan the workshop.",
"mode": "queue",
"payload": { "workshopId": "ws_812", "audience": "backend" }
}'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.
| Field | Meaning |
|---|---|
input | The user text for this turn. Required, not empty. |
idempotencyKey | Optional. The key in body form, for a proxied browser send; equal to the header when both are present. |
mode | queue (default): run after earlier turns. steer: deliver into the running turn when the runtime supports it, otherwise queue. interrupt: interrupt running turns and run next, with the same settlement behavior as the interrupt route. |
payload | Optional. A JSON value for the agent, at most 32 KB encoded. It reaches the agent as useInput().payload with source: "user", next to the text, and is recorded on the turn’s message.received event. The text stays the message; the payload is structured context the agent reads. |
answers | Optional. The id of the session’s open question. The input answers it, and the agent reads it as useInput().answer. |
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.
A turn sent to a suspended session resumes it: the turn is queued while
the session is resuming, and runs once the runtime reconnects. There is no
need to resume the session first.
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:
POST /sessionswithIdempotency-Key: <submission-id>andagentId: <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.POST /sessions/<id>/turnswithIdempotency-Key: <submission-id>/start, the text and thepayload.
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
curl 'https://app.opencomputer.dev/api/managed-agents/sessions/<session-id>/events?after=0' \
-H 'x-api-key: <api-key>'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.
| Operation | Method and path | Success |
|---|---|---|
| Resource inventory | GET /projects/<p>/memory | 200 with { resources } |
| List documents | GET /projects/<p>/memory/<r>/documents | 200 with { documents, nextCursor } |
| Read | GET /projects/<p>/memory/<r>/documents/<id> | 200 with the document and ETag |
| Create | PUT /projects/<p>/memory/<r>/documents/<id> with If-None-Match: * | 201 |
| Replace text or summary | PUT /projects/<p>/memory/<r>/documents/<id> with If-Match | 200 |
| Change title or policy | PATCH /projects/<p>/memory/<r>/documents/<id> with If-Match | 200 |
| Delete | DELETE /projects/<p>/memory/<r>/documents/<id> with If-Match | 204 |
curl -X PUT 'https://app.opencomputer.dev/api/managed-agents/projects/<project-id>/memory/notes/documents/workshop?environment=development' \
-H 'x-api-key: <api-key>' \
-H 'Content-Type: application/json' \
-H 'If-None-Match: *' \
-d '{ "title": "Workshop notes" }'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.
| Operation | Method and path | Body or query | Success |
|---|---|---|---|
| List | GET /projects/<p>/webhooks | Optional ?environment= and ?agentId= | 200 with { webhooks }, URLs without tokens |
| Create | POST /projects/<p>/webhooks | { name, agentId, environment, identity? } | 201 with { webhook }; token and the full invocationUrl appear once |
| Update | PATCH /projects/<p>/webhooks/<id> | Any of name, enabled, identity (null removes it) | 200 with { webhook } |
| Rotate token | POST /projects/<p>/webhooks/<id>/rotate-token | None | 200 with { webhook } carrying the new token |
| Delete | DELETE /projects/<p>/webhooks/<id> | None | 204 |
| Request ledger | GET /projects/<p>/webhooks/<id>/requests | None | 200 with { requests } |
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.
curl 'https://app.opencomputer.dev/api/managed-agents/projects/<project-id>/webhooks/<webhook-id>/requests' \
-H 'x-api-key: <api-key>'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.
| Operation | Method and path | Success |
|---|---|---|
| Create | POST /projects/<p>/event-subscriptions | 201 with { subscription } |
| List | GET /projects/<p>/event-subscriptions | 200 with { subscriptions } |
| Get | GET /projects/<p>/event-subscriptions/<id> | 200 with { subscription } |
| Delete | DELETE /projects/<p>/event-subscriptions/<id> | 204; pending deliveries stop |
curl -X POST 'https://app.opencomputer.dev/api/managed-agents/projects/<project-id>/event-subscriptions' \
-H 'x-api-key: <api-key>' \
-H 'Content-Type: application/json' \
-d '{
"agentId": "worker",
"events": ["turn.completed", "turn.failed"],
"destination": { "type": "session", "sessionId": "<coordinator-session-id>" },
"environment": "development"
}'| Field | Meaning |
|---|---|
agentId | Optional. Outcomes of this agent only; omitted, every agent in the project. |
events | One or more of turn.completed, turn.failed, turn.cancelled. Turn outcomes, not session ends. |
destination | { type: "session", sessionId }: a session of an agent in this project that can still take turns. |
environment | development or production. A subscription is scoped to one environment: it selects outcomes recorded there and its destination session runs there. |
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:
| Field | Meaning |
|---|---|
id | <subscription-id>:<event-id> |
subscriptionId, eventId, eventType | What was delivered, to which subscription |
destination | The subscription’s destination |
status | pending, delivered or failed |
attempt | Delivery attempts so far |
receipt | { sessionId, turnId }: the turn the destination admitted, once delivered |
nextAttemptAt | When a pending delivery is retried |
error | subscription_unavailable, target_missing, target_ended, or delivery_failed |
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.
| Operation | Method and path | Body or query | Success |
|---|---|---|---|
| Create | POST /projects/<p>/linear/connections | { name, environment, agentId } | 201 with { connectionId, webhookUrl, createAppUrl, connection }; 200 when a pending connection for that binding already exists |
| List | GET /projects/<p>/linear/connections | Optional ?environment= | 200 with { connections } |
| Set credentials | PUT /linear/connections/<id>/credentials | { clientId, clientSecret, signingSecret }, as copied from the Linear app | 200 with { connection } |
| Authorize | POST /linear/connections/<id>/authorize | None | 200 with { authorizeUrl, expiresAt } |
| Disconnect | DELETE /linear/connections/<id> | None | 200 with { connection, revoked } |
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.
| Code | Meaning |
|---|---|
400 invalid_environment, 400 invalid_agent | environment must be development or production; agentId must name an agent of the project |
400 invalid_linear_app_name | The name must be 1 to 64 characters |
400 linear_app_name_reserved | Linear does not allow app names that contain “Linear” |
400 invalid_linear_credentials | clientId, clientSecret and signingSecret are all required |
403 forbidden | A project-scoped key on a route other than List |
404 project_not_found | No such project for this key |
404 linear_connection_not_found | No such connection, or it was disconnected |
409 project_archived | Restore the project before creating a connection |
409 linear_already_connected | The agent already has a connection in this environment; error.connectionId names it |
409 linear_app_already_connected | That Linear app is connected to another agent or environment; create a separate app |
409 linear_connection_connected | The connection is authorized with a different Linear app; disconnect it first |
409 linear_credentials_required | Set the credentials before authorizing |
409 linear_connection_changed | Another request changed the connection; list the connections and continue; error.connectionId names it |
503 linear_connector_unavailable | Linear connections are not available right now |
Projects, agents and deployments
| Operation | Method and path | Success |
|---|---|---|
| List projects | GET /projects | 200 with { projects } |
| Create project | POST /projects with { name, slug?, environmentMode? } | 201 with the project. environmentMode is single (one default environment) or legacy (Development and Production) |
| Get project | GET /projects/<p> | 200 with { project, deployments, sessions, connections, schedules } |
| List agents | GET /agents | 200 with { agents }: id, name, activeAlias, activeDeploymentId, deploymentCount; activeAlias and activeDeploymentId are null while the agent has no active deployment |
| List deployments | GET /deployments?agentId=<agent-id> | 200 with { deployments } |
| Get deployment | GET /deployments/<id> | 200 with id, agentId, alias, memory declarations, createdAt |
| List GitHub repositories | GET /projects/<p>/github/repositories?environment=<env> | 200 with { repositories, nextCursor }; see GitHub App connections |
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.
curl 'https://app.opencomputer.dev/api/managed-agents/projects' \
-H 'x-api-key: <api-key>'Secrets, runtime variables, schedules and logs are managed with the CLI and the dashboard; see their pages under Concepts.