Skip to content

API Reference

The SAM API runs on a Cloudflare Worker at api.{domain}. All authenticated endpoints require a valid BetterAuth session cookie.

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.

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.

MethodEndpointPurpose
GET/api/projects/:projectId/event-subscriptionsList subscriptions; optional sessionId, state and limit
GET/api/projects/:projectId/event-subscriptions/:subscriptionIdInspect one subscription
GET/api/projects/:projectId/event-subscriptions/:subscriptionId/deliveriesInspect bounded recent transport outcomes; optional limit
POST/api/projects/:projectId/event-subscriptions/:subscriptionId/cancelCancel 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.

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.

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.

Start GitHub OAuth flow. Redirects to GitHub for authorization.

End the current session.

Returns the current authenticated session and user info.

Create a new workspace.

Body:

{
"installationId": "12345",
"repository": "owner/repo",
"branch": "main",
"vmSize": "medium",
"displayName": "My Workspace"
}

List all workspaces for the authenticated user.

Get workspace details including status, node info, and URLs.

Permanently stop a running workspace and delete any retained persistent-session snapshot. Use sleep when the same chat must be resumable.

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.

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.

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.

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 the provisioning progress log for a workspace.

Create a new agent session in a workspace. The agent (Claude Code, Codex, Gemini CLI, etc.) is determined by the selected agent profile.

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.

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.

MethodEndpointPurpose
GET/api/projects/:id/sessions/:sessionId/resource-historyHistory scoped to one chat session
GET/api/projects/:id/tasks/:taskId/resource-historyHistory scoped to one task
GET/api/projects/:id/workspaces/:workspaceId/resource-historyHistory 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.

The Resources drawer reads a whole chat session through two endpoints, both scoped to the project and session in the path.

MethodEndpointPurpose
GET/api/projects/:id/sessions/:sessionId/resource-timelineEvery retained chunk of the session, with rollups
GET/api/projects/:id/sessions/:sessionId/resource-timeline/chunks/:chunkIdOne 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.

The permission requests, questions, and links an agent puts in a chat while it waits (see When the Agent Needs You).

MethodEndpointPurpose
GET/api/projects/:id/sessions/:sessionId/interactionsPending and recently settled requests for the chat
GET/api/projects/:id/sessions/:sessionId/interactions/:interactionIdOne request’s full detail (session creator only)
POST/api/projects/:id/sessions/:sessionId/interactions/:interactionId/answerAnswer 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.

List all nodes for the authenticated user.

Get node details including health status and hosted workspaces.

Stop a running node. All workspaces on the node must be stopped first.

Delete a node and clean up DNS records and Hetzner resources.

Add or update a credential (cloud provider token or agent API key).

Body:

{
"provider": "hetzner",
"credentialType": "cloud-provider",
"token": "your-api-token"
}

List all credentials for the authenticated user (tokens are not returned).

Delete a stored cloud-provider credential.

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.

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.

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:

ParameterValuesDescription
scopeuser, project, installationSelects the credential scope to inspect. Omit it for the authenticated user’s personal catalog.
projectIdProject IDRequired 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.

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.

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.

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.

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.

Update only the installation-owned default-pool policy, candidate statuses, or provider-native catalogAdditions. Superadmin only.

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.

Post-installation redirect handler. Records the installation and redirects to Settings.

List all projects for the authenticated user.

Create a new project linked to a GitHub repository.

Get project details.

Create a task record.

Body:

{
"title": "Fix the login button"
}

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.

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.

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.

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.

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.

JSON Web Key Set for JWT verification by VM Agents.

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.

See Reporting Issues for the user-facing flow and the operator configuration these endpoints depend on.

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.

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.

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.

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".

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.

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.

Read the stored allowance (or null) and the effective budget ceilings.

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.

Remove the allowance; the user reverts to platform defaults and every tier.