API Reference
The SAM API runs on a Cloudflare Worker at api.{domain}. All authenticated endpoints require a valid BetterAuth session cookie.
MCP orchestration
Section titled “MCP orchestration”Task agents can call wait_for_subtasks with a stable workflow-step waitKey, unique same-project task IDs, an optional condition of all (the default) or any, and an optional bounded wakeAfterSeconds. The agent must persist the workflow state and key before calling, reuse the key after a lost response, and end its turn after registration. ProjectData then reconciles selected task terminal state and durably wakes the same canonical caller session exactly once, including after session sleep and runtime replacement. Automatic wake prompts carry only trusted task IDs and statuses; agents fetch peer-authored output explicitly as untrusted data. Servers without durable prompt delivery reject registration so clients can use bounded foreground polling as a compatibility fallback.
Private feedback-project agents can also call list_incident_queue, get_incident, claim_incident, and resolve_incident. These tools are scoped server-side to the effective private feedback project setting (Admin → Integrations runtime value first, then PLATFORM_FEEDBACK_PROJECT_ID fallback); callers cannot provide a project id. claim_incident and resolve_incident require a task-scoped MCP token, use bounded leases/CAS tokens, and return only private redacted incident evidence labelled as untrusted. resolve_incident accepts structured ship-or-track fields for resolved outcomes: fixPrUrl, dispatchedTaskId, or linkedRecordId; rejected outcomes require a justification note instead.
Project eventing uses a pull loop over the canonical ProjectData project_event_* tables. Agents create a short-lived subscription with create_project_event_subscription, using v1 exact/set filters for source, eventType, subjectType, subjectId, and severity; recover existing subscriptions with list_project_event_subscriptions; inspect or cancel with get_project_event_subscription and cancel_project_event_subscription. Project, task, session, workspace, owner, and agent identity come from the verified MCP token, not tool arguments.
dispatch_task accepts modern VM workload input through resourceRequirements with optional minVcpu, minMemoryGb, minDiskGb, and exclusiveNode. Known numeric fields must be finite and non-negative; exclusiveNode: false is preserved. Deprecated vmSize remains accepted as a legacy compatibility hint and is not expanded into hardware by the client or MCP handler.
dispatch_task also accepts an optional coordinationChannel: an ordinary project event channel name (agent-dm. is reserved) shared by a feature’s agents. When omitted, the child inherits the dispatching task’s channel, so every descendant shares one channel; retry_subtask and session recovery copy it onto replacement tasks. The child’s description gains a short “Coordination channel” section and get_instructions returns task.coordinationChannel with guidance. Channels stay project-visible, so this routes work and is not an access boundary. Implementation: parseCoordinationChannelParam in apps/api/src/routes/mcp/dispatch-coordination-channel.ts.
After subscribing, call list_subscription_events with the subscriptionId to replay missed or queued matches. The list response is payload-free: it returns summaries, delivery IDs, delivery state, hasMore, and an opaque nextCursor that is valid only for the same subscription. Call get_event only when a summary needs full stored event details, then call ack_event_delivery after processing each returned deliveryId; ack is idempotent. The pull tools themselves record matches, delivery decisions, and pull acknowledgements only — they do not steer runtimes, spawn tasks, or expose human/UI controls. Delivery happens out of band: existing_session_prompt and runtime_interrupt subscriptions wake the target chat through the durable prompt queue, and runtime_interrupt wakes carry the interrupt mailbox class, which may cancel an in-flight turn so the wake is delivered immediately.
Event subscriptions
Section titled “Event subscriptions”Project members with read access can inspect subscriptions. Members with task write access can cancel human or agent subscriptions. System, policy and standing-watch subscriptions must be managed through their owning controls.
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /api/projects/:projectId/event-subscriptions | List subscriptions; optional sessionId, state and limit |
| GET | /api/projects/:projectId/event-subscriptions/:subscriptionId | Inspect one subscription |
| GET | /api/projects/:projectId/event-subscriptions/:subscriptionId/deliveries | Inspect bounded recent transport outcomes; optional limit |
| POST | /api/projects/:projectId/event-subscriptions/:subscriptionId/cancel | Cancel with optional { "reason": "No longer needed" } |
The list defaults to active subscriptions and returns { subscriptions, hasMore }. state accepts active, cancelled, expired or any. Results include ownership, target, requested and resolved delivery, expiry and cancellation details. Cancelling again is safe and reports idempotent: true; cancellation does not undo work an agent has already performed. The server derives the cancelling user’s identity from the authenticated session.
Implementation: projectEventSubscriptionRoutes uses the existing project capability checks and canonical subscription cancellation.
Agent event channels
Section titled “Agent event channels”Project members can browse channel activity with GET /api/projects/:projectId/event-channels
and read a channel with GET /api/projects/:projectId/event-channels/:channel/history.
Both require active membership with task:read. Each accepts limit and an optional
cursor; catalog responses return nextCursor, while history returns cursor,
hasMore, watermark, and retentionGap. Counts describe lifetime publications in
the current catalog generation, including events that retention has removed.
Agents use publish_channel_event, list_event_channels, get_channel_history,
follow_event_channel, and catch_up_event_channel through MCP. Publishing and
following require task:write; SAM verifies the calling task and derives its
project, user, chat and workspace identity. Messages are untrusted evidence.
They cannot override the reserved sam.agent_channel source or its event type.
To switch from history to live events, pass the consumed history cursor to
follow_event_channel, then call catch_up_event_channel until hasMore is false.
Read and acknowledge the resulting events with the ordinary subscription tools.
Concurrent publications are included through either catch-up or live matching.
Replaying a follow key retains the original watermark and deadline; a retention
gap or expired checkpoint requires a new explicit history/follow decision.
Omit the history cursor when only future events are wanted.
Following defaults to recording events. With requestedDelivery: existing_session_prompt,
follow and catch-up responses include the ordinary subscription checkpoint/end-turn
instructions and explain that a no-match expiry delivers no wake prompt.
Publishing the same message with the same key in the same chat/channel replays its retained event. Reusing that key with different content reports a conflict. Idempotency ends when event retention removes the record. Publication has a shared per-project fixed-window rate limit; a boundary burst can span two windows. Payload, fanout, channel cardinality and history page limits are configurable. Empty idle catalog generations may be reclaimed, while live subscriptions continue to follow the stable channel name.
A channel publication never matches the publisher’s own existing_session_prompt or
runtime_interrupt subscription, live or through catch-up, so publishing never wakes
the publisher (isSelfOriginatedChannelWake). Record-only followers still see their
own publications in their pull feed. Channel names starting with agent-dm. are
reserved for SAM agent messaging and are rejected by publish_channel_event.
Agent messages over shared channels
Section titled “Agent messages over shared channels”With AGENT_MESSAGE_CHANNELS_ENABLED=true, and only while event wakes and durable
prompt delivery are enabled, send_durable_message (classes notify and deliver)
and send_message_to_subtask stop injecting the sender’s text into the recipient’s
prompt. One ProjectData transaction (sendAgentChannelMessage) finds or creates the
canonical agent-dm.<pair digest> channel for the two chat sessions, ensures a
SAM-managed prompt-delivery subscription for each chat, and publishes the message
with its server-derived sender. The recipient is woken with a SAM-authored notice
that carries event IDs only; it reads the text with get_event
(event.metadata.actor is the verified sender), replies with send_durable_message,
and acknowledges with ack_event_delivery. Urgent classes keep stop-and-deliver.
Responses report accepted: true, delivered: false with transport, channel,
eventId and sequence; an optional idempotencyKey replays a lost-response retry,
and changed content under the same key is rejected as a conflict. Messages are capped
by PROJECT_EVENT_CHANNEL_MESSAGE_MAX_BYTES and pair channels by
AGENT_MESSAGE_CHANNEL_MAX_CHANNELS. Managed deployments enable the transport in wrangler.toml; the code fallback is off and AGENT_MESSAGE_CHANNELS_ENABLED=false selects the legacy path.
Pair-channel events match only subscriptions targeting one of the two participant
chats (subscriptionCanMatchProjectEvent), follow_event_channel rejects
agent-dm. names, and subscription idempotency keys starting with
sam-agent-message: are reserved, so other agents cannot be woken by a pair’s
messages. History stays project-visible through get_channel_history. Managed
subscriptions may hold at most AGENT_MESSAGE_MAX_ACTIVE_SUBSCRIPTIONS of the
project’s active-subscription cap; at that share SAM releases the least recently
matched idle pair subscriptions on other channels (the pair’s next message recreates
them), and refuses the send with outcome: "capacity" (retryable) while every
candidate still owes a wake. Failed sends report outcome as recipient_unavailable
(the recipient’s chat or authority is gone; not retryable), conflict, capacity or
rejected, and commit nothing. When a managed subscription exhausts its wake budget, its pending
wake remains deliverable while a replacement handles new messages. The drained old
subscription is retired after delivery.
Authentication
Section titled “Authentication”POST /api/auth/sign-in/social
Section titled “POST /api/auth/sign-in/social”Start GitHub OAuth flow. Redirects to GitHub for authorization.
POST /api/auth/sign-out
Section titled “POST /api/auth/sign-out”End the current session.
GET /api/auth/session
Section titled “GET /api/auth/session”Returns the current authenticated session and user info.
Workspaces
Section titled “Workspaces”POST /api/workspaces
Section titled “POST /api/workspaces”Create a new workspace.
Body:
{ "installationId": "12345", "repository": "owner/repo", "branch": "main", "vmSize": "medium", "displayName": "My Workspace"}GET /api/workspaces
Section titled “GET /api/workspaces”List all workspaces for the authenticated user.
GET /api/workspaces/:id
Section titled “GET /api/workspaces/:id”Get workspace details including status, node info, and URLs.
POST /api/workspaces/:id/stop
Section titled “POST /api/workspaces/:id/stop”Permanently stop a running workspace and delete any retained persistent-session snapshot. Use sleep when the same chat must be resumable.
POST /api/workspaces/:id/sleep
Section titled “POST /api/workspaces/:id/sleep”Checkpoint the workspace’s agent HOME, harness identity, exact Git checkout, and repository work in progress, verify the snapshot, and put the session to sleep. Git state includes the saved HEAD, branch or detached state, canonical upstream metadata, clean local-only commits, working tree, and index. VM compute is stopped only after SAM re-verifies the durable manifest and every artifact the manifest still claims. A complete snapshot restores and validates that state; if the saved Git state cannot be recreated, wake reports explicit degraded recovery instead of success on a different commit. Sleep requires a complete final snapshot (verifyAndBeginSleepTeardown in apps/api/src/services/session-sleep-execution.ts). If the final checkpoint is degraded, or stops reporting progress and is recorded as degraded, the sleep request fails and the workspace stays awake. SAM then retries automatically, within the bounded sleep-failure budget (SESSION_SLEEP_FAILURE_MAX_ATTEMPTS, SESSION_SLEEP_FAILURE_MAX_ELAPSED_MS); after that an idle VM session may sleep on a Git recovery point instead (SAM could not save a complete snapshot). A session that already sleeps on an older degraded snapshot still wakes and reports the reduced restore state. Sending a follow-up in the same chat wakes the session during the seven-day retention window.
POST /api/workspaces/:id/restart
Section titled “POST /api/workspaces/:id/restart”Restart a stopped or errored workspace. Provisions a new VM and recreates the container. A
restart can cancel an unclaimed pending deletion, but returns 409 once a deletion attempt
has started or if the workspace identity changes before runtime recreation.
POST /api/workspaces/:id/rebuild
Section titled “POST /api/workspaces/:id/rebuild”Rebuild a running, recovering, or errored workspace on its assigned node. Returns 202 with
{ "status": "rebuilding" } after the exact workspace transition is claimed. Rebuild uses
the same deletion fence as restart: it cannot cancel or cross a deletion attempt that has
already started, and the VM request is refused if the workspace identity or status changes.
DELETE /api/workspaces/:id
Section titled “DELETE /api/workspaces/:id”Permanently request deletion of a workspace, its retained session snapshot, and associated
resources. A confirmed deletion returns 200 with
{ "success": true, "deletionStatus": "confirmed" }. If VM deletion is not yet proven,
the endpoint returns 202 with
{ "success": true, "deletionStatus": "pending", "workspaceStatus": "stopping", "reason": "..." }.
The 202 response is not deletion proof: SAM keeps the workspace quarantined and retries from
durable state until VM absence/success or strict provider/container termination is confirmed.
The same 202 is returned for an idempotent repeat while that exact durable attempt is already
in flight, or when VM proof arrived but a concurrent state write requires terminalization to
converge on a later retry.
If the exact deletion target is missing, active, or reassigned before a durable attempt is
retained, the endpoint returns 409 with deletionStatus: "rejected"; it never labels that
rejection as a pending deletion.
GET /api/workspaces/:id/boot-log
Section titled “GET /api/workspaces/:id/boot-log”Get the provisioning progress log for a workspace.
Agent Sessions
Section titled “Agent Sessions”POST /api/workspaces/:id/agent-sessions
Section titled “POST /api/workspaces/:id/agent-sessions”Create a new agent session in a workspace. The agent (Claude Code, Codex, Gemini CLI, etc.) is determined by the selected agent profile.
GET /api/workspaces/:id/agent-sessions
Section titled “GET /api/workspaces/:id/agent-sessions”List active agent sessions for a workspace.
POST /api/workspaces/:id/agent-sessions/:sessionId/stop
Section titled “POST /api/workspaces/:id/agent-sessions/:sessionId/stop”Stop a running agent session.
Resource history
Section titled “Resource history”Retained CPU, memory, I/O, and out-of-memory observations for VM-backed workspaces. Requires project access. See Session Resource History for how to read the values.
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /api/projects/:id/sessions/:sessionId/resource-history | History scoped to one chat session |
| GET | /api/projects/:id/tasks/:taskId/resource-history | History scoped to one task |
| GET | /api/projects/:id/workspaces/:workspaceId/resource-history | History scoped to one workspace |
All three return { summary, chunks }, where summary carries the session’s peaks, sample and gap counts, I/O totals, OOM count, and nullable agentProfileId, skillId, and agentType attribution. SAM resolves those attribution fields from server-owned records scoped to the same project and workspace; upload values cannot override them. memoryWorkingSetMeanBytes and memoryWorkingSetPeakBytes exclude reclaimable inactive file cache and are the sizing figures; they are null for history uploaded by older VM agents. memoryMeanBytes, memoryPeakBytes, and memoryKernelPeakBytes remain cache-inclusive for comparison. chunks is the index of retained time slices, newest first and capped at WORKSPACE_RESOURCE_LIST_LIMIT (24) with no cursor or offset — ?chunkId= still resolves a chunk outside that window, but nothing enumerates the older IDs. Add ?chunkId= to include a detail object with that chunk’s samples and tool-correlation spans, downsampled to WORKSPACE_RESOURCE_DETAIL_MAX_POINTS (720) with spikes preserved. Each sample can include memoryWorkingSetBytes; when it is absent, that sample’s working set is unknown rather than zero. Each detail.toolSpans entry may include an ACP kind and metadata-provided toolName; either can be absent in history from older VM agents. Tool names are capped by WORKSPACE_RESOURCE_TOOL_NAME_MAX_BYTES (256 by default).
Samples are workspace-cgroup observations, not per-process attribution. Stored payloads deliberately exclude prompts, tool-call titles, commands, tool inputs and arguments, tool output, file paths, environment values, and secrets; tool-call IDs are hashed. A tool name identifies the tool implementation, such as Bash, without exposing what it ran. Instant (Cloudflare Container) sessions have no resource history: the response is 200 with summary: null and an empty chunks array.
Session timeline
Section titled “Session timeline”The Resources drawer reads a whole chat session through two endpoints, both scoped to the project and session in the path.
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /api/projects/:id/sessions/:sessionId/resource-timeline | Every retained chunk of the session, with rollups |
| GET | /api/projects/:id/sessions/:sessionId/resource-timeline/chunks/:chunkId | One chunk’s 5-second samples and tool spans |
The index returns { sessionId, runs, chunks, totalChunkCount, omittedChunkCount, maxChunks, collection, runtime }. chunks is ascending by start time and covers every workspace the session ran on (each wake is a new workspace); each carries its summary and a columnar per-minute rollup (null for chunks uploaded before rollups existed). runs groups chunks by workspace with that workspace’s reservation (cpuMillis, memoryMb, or null). Past WORKSPACE_RESOURCE_TIMELINE_MAX_CHUNKS (1000) the oldest chunks are left out and counted in omittedChunkCount. When there are no chunks, collection explains why: unsupported (an Instant session), expired (samples passed retention; the summary remains), or pending (nothing uploaded yet). The chunk endpoint returns the same shape as detail above and 404s for a chunk that belongs to another project or session.
Agents read the same data with the get_resource_history MCP tool, which takes no projectId — the project comes from the verified token. With no arguments it returns the caller’s own session; supplying any one of sessionId, taskId, or workspaceId replaces the caller’s defaults entirely rather than narrowing within them.
Agent requests
Section titled “Agent requests”The permission requests, questions, and links an agent puts in a chat while it waits (see When the Agent Needs You).
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /api/projects/:id/sessions/:sessionId/interactions | Pending and recently settled requests for the chat |
| GET | /api/projects/:id/sessions/:sessionId/interactions/:interactionId | One request’s full detail (session creator only) |
| POST | /api/projects/:id/sessions/:sessionId/interactions/:interactionId/answer | Answer a pending request (session creator only) |
The list returns { pending, settled, cursor } and needs task:read on the project. Each item has the request’s interactionId, kind (permission, form, or url), state, createdAt, and deadlineAt; for other project members that is all a pending item carries, and settled is empty — they never see the question or the answer. The detail is for the session creator only, decrypted on request, and served with Cache-Control: private, no-store. Answering needs task:write, must come from the SAM web app’s origin (the Origin header must match), and sends an answerKey with a decision: { kind: "selected_option", optionId } for a permission, { kind: "accepted", content } for a question (content holds the form’s values) or { kind: "accepted" } for a link, or { kind: "declined" }. Each decision also carries an answerHash: the SHA-256 hex of the option ID, of the form values as key-sorted JSON, or of the word accepted or declined. Sending the same answerKey and decision again is safe; answering a request that was already answered or has expired returns 409.
GET /api/nodes
Section titled “GET /api/nodes”List all nodes for the authenticated user.
GET /api/nodes/:id
Section titled “GET /api/nodes/:id”Get node details including health status and hosted workspaces.
POST /api/nodes/:id/stop
Section titled “POST /api/nodes/:id/stop”Stop a running node. All workspaces on the node must be stopped first.
DELETE /api/nodes/:id
Section titled “DELETE /api/nodes/:id”Delete a node and clean up DNS records and Hetzner resources.
Credentials
Section titled “Credentials”POST /api/credentials
Section titled “POST /api/credentials”Add or update a credential (cloud provider token or agent API key).
Body:
{ "provider": "hetzner", "credentialType": "cloud-provider", "token": "your-api-token"}GET /api/credentials
Section titled “GET /api/credentials”List all credentials for the authenticated user (tokens are not returned).
DELETE /api/credentials/:provider
Section titled “DELETE /api/credentials/:provider”Delete a stored cloud-provider credential.
GET /api/credentials/limits
Section titled “GET /api/credentials/limits”Latest provider usage windows for the authenticated user’s personal credentials, across projects
(newest sample per credential and window). Each credential carries credentialId (for
cc_credentials:<id> references), level (ok, warning, critical, rejected) and its
windows (windowType, utilizationPercent, windowMinutes, resetsAt, observedAt, source).
Rows come from credential_limit_windows; the response is capped by CREDENTIAL_LIMIT_READ_MAX_ROWS.
GET /api/projects/:id/credential-limits
Section titled “GET /api/projects/:id/credential-limits”Usage windows visible to the caller inside a project: the caller’s own credentials plus project-
and platform-shared ones, never another member’s personal credential. Requires project:read.
Optional agentSessionId narrows the result to the credential that agent session is attributed to,
resolved server-side from agent_sessions. The MCP tool get_credential_limits exposes the same
view to agents (scope: "session" | "project"). credentialId is filled in for the caller’s own
credentials; a shared credential owned by another member whose reference is longer than 71 bytes
comes back with a sha256: digest as credentialReference and credentialId: null, with or
without agentSessionId.
GET /api/providers/catalog
Section titled “GET /api/providers/catalog”List non-secret compute-provider catalog metadata for cloud-provider credentials the caller can use.
The response includes provider-native instance offerings, locations, normalized resource metadata,
price metadata when available, and a catalogSource value such as api or static.
Optional query parameters:
| Parameter | Values | Description |
|---|---|---|
scope | user, project, installation | Selects the credential scope to inspect. Omit it for the authenticated user’s personal catalog. |
projectId | Project ID | Required when scope=project; the caller must have the project secret:read capability. |
Installation scope is restricted to superadmins and uses enabled platform compute credentials.
User scope uses the caller’s active personal compute credentials. Project scope returns active
compute credentials attached to that project after project secret:read authorization, including
composable-credential attachments from other project members. Effective default pool summaries expose
project → user → installation fallback separately.
If no credentials are visible for the requested scope, the response still returns catalogs: []
with credentialSetupRequired: true and a credentialSetupMessage suitable for setup UI.
Capacity pools
Section titled “Capacity pools”See the Compute Pools guide for what these concepts mean and how they are surfaced in the app.
Default capacity-pool endpoints expose non-secret pool, source, and concrete candidate metadata.
Responses have this shape: effective, effectiveScope, effectiveState, defaults,
precedence, reconciledScopes, policyMutationSupported, and placementSettings. Each summary
includes activeCandidateCount, availableCandidateCount, effectiveState, and non-secret
diagnostics. placementSettings is a redacted settings contract containing the effective settings
fingerprint, selected source labels, selection weights, rollout percent, and normalized
resourceDefaults.legacyWorkloadMapping plus resourceDefaults.platformDefaults; it does not
include internal diagnostics or credential/source identifiers. Use ensure=true on GET endpoints,
or call the matching /reconcile endpoint, to refresh pool metadata from the credential-scoped
provider-native catalog. Provider API failures are reported in catalog refresh metadata and do not
synthesize static available rows for reconciliation. Provider catalog offerings expose
catalogSource; capacity-pool candidates expose the persisted providerInstanceCatalogSource and
catalogAvailability snapshot. sourceGeneration and catalogGeneration are refresh/fencing
epochs and may advance on identical reconciliations. authorityGeneration on sources and
candidates is a stable semantic authority value for the selected credential/source/offering state.
Placement snapshots persist capacityAuthorityGeneration; the legacy sourceGeneration snapshot
field is a compatibility alias for that same authority value, not the refresh epoch.
Only an unconfigured scope inherits from the next default-pool scope. A configured project or user
pool with state configured-empty, source-disabled, catalog-unavailable, or migration-pending
remains authoritative and is returned as effective; placement then queues/fails inside that pool
according to its exhaustion policy.
GET /api/capacity-pools/defaults
Section titled “GET /api/capacity-pools/defaults”Read the authenticated user’s default compute pool. Optional ensure=true reconciles it from the
user’s active personal compute credentials. The response includes effectiveSummary, a redacted
project/user/installation selection summary with no credential IDs or source metadata. Configured
zero-active or catalog-unavailable owned pools remain visible and are still selected as effective
so clients do not infer unsupported cross-scope fallback. Ordinary users may receive an
installation-funded effectiveSummary from existing installation metadata, but this endpoint never
reconciles installation credentials.
When the effective pool is ready, effectiveSummary.nativeOfferings lists its eligible VM
provider, location, native instance type, display name, resources and displayed price. These
choices omit pool, source, credential and owner identifiers. The Nodes creation form uses them
even when the user has no personal cloud credential. Empty, disabled or migrating effective
pools return no choices; they do not fall back to a personal credential catalog. Final node
creation still revalidates the selected offering against current pool authority.
POST /api/capacity-pools/defaults/reconcile
Section titled “POST /api/capacity-pools/defaults/reconcile”Explicitly reconcile the authenticated user’s default pool from active personal compute credentials.
PATCH /api/capacity-pools/defaults
Section titled “PATCH /api/capacity-pools/defaults”Update the authenticated user’s owned default-pool policy, candidate statuses, or provider-native
catalogAdditions. This endpoint does not mutate project or installation fallback pools.
catalogAdditions reactivates a currently available provider-native offering from the same
credential-scoped catalog used by reconciliation. It identifies the active source plus concrete
provider/location/instance type; it does not accept secret material:
{ "catalogAdditions": [ { "sourceId": "cap-source-default:user:user-credential-id", "provider": "hetzner", "location": "fsn1", "providerInstanceType": "cpx62", "providerInstanceSku": null } ]}GET /api/projects/:id/capacity-pools/defaults
Section titled “GET /api/projects/:id/capacity-pools/defaults”Read a project’s default pool context. Requires project project:read. All members receive
effectiveSummary, a redacted authoritative project → user → installation selection summary with no
credential IDs or source metadata. Raw project/user summaries require project secret:read; raw
installation details remain superadmin-only. Optional ensure=true reconciles only when the caller
also has project secret:read, and non-superadmins never reconcile installation credentials through
this route.
POST /api/projects/:id/capacity-pools/defaults/reconcile
Section titled “POST /api/projects/:id/capacity-pools/defaults/reconcile”Explicitly reconcile visible default capacity-pool metadata from existing credentials in the project
context. Project-scoped metadata comes from active project compute credentials. Requires project
secret:read.
PATCH /api/projects/:id/capacity-pools/defaults
Section titled “PATCH /api/projects/:id/capacity-pools/defaults”Update only the project-owned default-pool policy, candidate statuses, or provider-native
catalogAdditions. Requires project secret:write.
GET /api/admin/capacity-pools/defaults
Section titled “GET /api/admin/capacity-pools/defaults”Read the installation default pool. Superadmin only. Optional ensure=true reconciles enabled
platform compute credentials.
POST /api/admin/capacity-pools/defaults/reconcile
Section titled “POST /api/admin/capacity-pools/defaults/reconcile”Explicitly reconcile the installation default pool from enabled platform compute credentials. Superadmin only.
PATCH /api/admin/capacity-pools/defaults
Section titled “PATCH /api/admin/capacity-pools/defaults”Update only the installation-owned default-pool policy, candidate statuses, or provider-native
catalogAdditions. Superadmin only.
GitHub
Section titled “GitHub”GET /api/github/installations
Section titled “GET /api/github/installations”List GitHub App installations for the authenticated user.
GET /api/github/repositories?installation_id=:id
Section titled “GET /api/github/repositories?installation_id=:id”List repositories accessible through a GitHub App installation.
GET /api/github/callback
Section titled “GET /api/github/callback”Post-installation redirect handler. Records the installation and redirects to Settings.
Projects
Section titled “Projects”GET /api/projects
Section titled “GET /api/projects”List all projects for the authenticated user.
POST /api/projects
Section titled “POST /api/projects”Create a new project linked to a GitHub repository.
GET /api/projects/:id
Section titled “GET /api/projects/:id”Get project details.
POST /api/projects/:id/tasks
Section titled “POST /api/projects/:id/tasks”Create a task record.
Body:
{ "title": "Fix the login button"}Deployment Releases
Section titled “Deployment Releases”POST /api/projects/:projectId/environments/:envId/releases
Section titled “POST /api/projects/:projectId/environments/:envId/releases”Create a deployment release for an environment.
Preferred body: Docker Compose YAML with Content-Type: text/yaml, application/yaml, text/x-yaml, or application/x-yaml. Compose submissions may use x-sam-routes for routes and x-sam-secret environment values for secret references.
Raw manifest JSON is still accepted for backward compatibility when another content type is used.
POST /api/projects/:id/tasks/submit
Section titled “POST /api/projects/:id/tasks/submit”Submit an idea for autonomous execution. This is the chat-first path used by the web app; it creates the task, records the first message, and starts execution.
Body:
{ "message": "Fix the login button on the settings page", "resourceRequirements": { "minVcpu": 4, "minMemoryGb": 16, "exclusiveNode": false }}resourceRequirements is additive modern workload input. The API validates known fields and persists the normalized JSON through task launch inputs. Legacy vmSize is still accepted for old clients as a deprecated compatibility hint; clients should not infer provider hardware from it. Profiles, skills, and triggers persist the same modern data as resourceRequirementsJson for compatibility, with explicit null clearing that layer and omitted fields preserving inherited behavior.
File Proxy (Project Chat)
Section titled “File Proxy (Project Chat)”These endpoints proxy file operations to the workspace’s VM agent, accessed through a project chat session.
GET /api/projects/:id/sessions/:sessionId/files/list
Section titled “GET /api/projects/:id/sessions/:sessionId/files/list”List files in a workspace directory.
GET /api/projects/:id/sessions/:sessionId/files/view
Section titled “GET /api/projects/:id/sessions/:sessionId/files/view”View the contents of a file in the workspace.
POST /api/projects/:id/sessions/:sessionId/files/upload
Section titled “POST /api/projects/:id/sessions/:sessionId/files/upload”Upload files to the workspace container (multipart form data).
GET /api/projects/:id/sessions/:sessionId/files/download
Section titled “GET /api/projects/:id/sessions/:sessionId/files/download”Download a file from the workspace container.
GET /api/projects/:id/sessions/:sessionId/files/raw
Section titled “GET /api/projects/:id/sessions/:sessionId/files/raw”Stream a binary file (images, etc.) with MIME detection and ETag support.
GET /api/projects/:id/sessions/:sessionId/git/status
Section titled “GET /api/projects/:id/sessions/:sessionId/git/status”Get git status of the workspace repository.
GET /api/projects/:id/sessions/:sessionId/git/diff
Section titled “GET /api/projects/:id/sessions/:sessionId/git/diff”Get git diff output for the workspace repository.
Terminal
Section titled “Terminal”POST /api/terminal/token
Section titled “POST /api/terminal/token”Generate a short-lived JWT for WebSocket terminal access.
Body:
{ "workspaceId": "ws-abc123"}Response:
{ "token": "eyJhbG...", "expiresAt": 1730000000000, "workspaceUrl": "https://ws-abc123.example.com"}The client opens the terminal WebSocket at wss://ws-abc123.example.com/terminal/ws/multi?token=... using the returned token.
Utility
Section titled “Utility”GET /health
Section titled “GET /health”Public health check endpoint. Returns { "status": "healthy", "timestamp": "..." } (or "degraded" with a 503 when critical bindings are unavailable). No version or internal details are exposed.
GET /.well-known/jwks.json
Section titled “GET /.well-known/jwks.json”JSON Web Key Set for JWT verification by VM Agents.
GET /api/agent/download
Section titled “GET /api/agent/download”Download the VM Agent binary. Query params: os (linux), arch (amd64, arm64), and an optional release containing exactly 40 lowercase hexadecimal characters. Invalid releases return 400 INVALID_VERSION before R2 is read. Official deployments use release so a new VM downloads the immutable binary selected by the live control plane; versioned responses use a one-year immutable cache policy. The unversioned URL retains its one-hour cache policy for legacy, local, manual, and intentional skip_agent deployments.
Used by cloud-init during VM (BYOC) provisioning. The Cloudflare Container instant-session runtime does not call this endpoint — its vm-agent binary is baked into the container image at deploy time.
POST /api/projects/:projectId/sessions/start
Section titled “POST /api/projects/:projectId/sessions/start”Start an Instant chat session. Returns 202 with { status: 'starting', runtime, taskId, sessionId, workspaceId, nodeId, workspaceUrl } as soon as the records exist; the container launch, repository clone, agent start, and first prompt continue in the background, so a client disconnect no longer strands the session.
Because the launch is asynchronous, the response does not carry agentSessionId, acpSessionId, or launch timings — poll the session or connect to its stream for live state.
This endpoint is Instant-only. It returns 409 (Selected profile resolves to VM runtime; use task submission instead.) whenever the runtime resolves to VM — because the profile pins vm, because the caller has their own or a project cloud credential, or because CF_CONTAINER_ENABLED is not true for the deployment. Use POST /api/projects/:id/tasks/submit for VM work.
Issue Reports
Section titled “Issue Reports”See Reporting Issues for the user-facing flow and the operator configuration these endpoints depend on.
GET /api/report-issue/config
Section titled “GET /api/report-issue/config”Report whether in-app issue reporting is available on this deployment. Returns { "enabled": false } when no effective private feedback project is configured, or when the effective setting does not name an existing project — the UI hides both entry points in that case.
POST /api/report-issue
Section titled “POST /api/report-issue”Submit an issue report. Returns 201 with the grouped incident’s private draft Idea ideaId, its status, and attachedRefKeys listing the technical references that were actually stored. Repeated matching reports update the same grouped incident/Idea instead of creating one Idea per occurrence.
Body fields: title, description, consentToAttachRefs, and an optional refs object (sessionId, taskId, nodeId, errorId, diagnosisId). References are only stored when consentToAttachRefs is true, and each is re-checked against the caller’s access first — unauthorized references are dropped silently rather than rejecting the request, so attachedRefKeys may be shorter than what was sent.
Rate-limited to 20 submissions per clock hour per user (RATE_LIMIT_REPORT_ISSUE_POST); exceeding it returns 429.
POST /api/admin/observability/feedback-triage
Section titled “POST /api/admin/observability/feedback-triage”Superadmin only. Runs a platform-error triage sweep immediately instead of waiting for the hourly cron. The feedback project itself is configured from Admin → Integrations or the environment fallback; this endpoint remains the manual way to verify automated triage without waiting up to an hour.
Admin Runtime Controls
Section titled “Admin Runtime Controls”These endpoints require an approved, authenticated superadmin. The switches are availability brakes: an absent KV key or KV read error means enabled (fail-open), so a transient KV outage does not silently halt recovery work.
GET /api/admin/runtime-controls
Section titled “GET /api/admin/runtime-controls”Read the cron-sweep and Durable Object alarm switches. The response includes
cronSweepsEnabled, doAlarmsEnabled, the resolved KV keys, the in-memory
cache TTL, the disabled-alarm retry interval, and semantics: "fail-open".
PATCH /api/admin/runtime-controls
Section titled “PATCH /api/admin/runtime-controls”Update either or both switches. At least one field is required and every provided value must be boolean.
{ "cronSweepsEnabled": false, "doAlarmsEnabled": false}Disabling the alarm switch does not drop alarm chains: each affected Durable Object re-arms at the reported safe retry interval and resumes normally after the switch is enabled again.
Admin AI Allowances
Section titled “Admin AI Allowances”These endpoints require an approved, authenticated superadmin. An allowance caps what a user
may set as their own SAM-mode AI budget and, through allowedModelTiers, which budget tiers of
the platform model catalog they may use at all.
GET /api/admin/ai-allowance/:userId
Section titled “GET /api/admin/ai-allowance/:userId”Read the stored allowance (or null) and the effective budget ceilings.
PUT /api/admin/ai-allowance/:userId
Section titled “PUT /api/admin/ai-allowance/:userId”Set or update fields; omitted fields keep their stored value.
{ "maxDailyInputTokens": 1000000, "maxDailyOutputTokens": null, "maxMonthlyCostCapUsd": 25, "allowedModelTiers": ["low-cost", "standard"]}allowedModelTiers is null (every tier) or an array drawn from low-cost, standard and
premium; any other value returns 400, and [] allows no platform model. The AI proxy enforces
it on every route that spends platform credentials — /ai/v1/chat/completions,
/ai/v1/responses, /ai/anthropic/v1/messages and /ai/anthropic/v1/messages/count_tokens —
before the request reaches the upstream provider. A model outside the allowed tiers, or one the
platform catalog does not assign a tier, returns 403 with a permission_error naming the model
and the allowed tiers; an allowance the proxy cannot read is refused rather than treated as
unrestricted. BYO-key passthrough (/ai/proxy/:wstoken/…) is not restricted, because it spends the
user’s own provider credential.
Before the tier check, each of those routes also requires the model to be on the operator’s
allowlist, AI_PROXY_ALLOWED_MODELS (by default every model in the platform catalog), whether or
not the user has an allowance; any other model returns 400 with an invalid_request_error.
Allowances live in Workers KV, so a change can take a minute or more to reach every Cloudflare
location.
DELETE /api/admin/ai-allowance/:userId
Section titled “DELETE /api/admin/ai-allowance/:userId”Remove the allowance; the user reverts to platform defaults and every tier.