HTTP API endpoints
Every public Shardflux HTTP API operation by resource: method, path, authentication, parameters and request fields, from openapi.json.
Every operation of the public HTTP API, generated from openapi.json (OpenAPI 3.1.0, API version 0.1.0). Base URL: https://api.shardflux.dev. Send a project API key on every request: Authorization: Bearer sfk_<key_id>_<secret>. Conventions (idempotency, pagination, long-running operations) are on the HTTP API page. Error responses use the ErrorBody schema; the codes are listed under Errors.
Workspace tools (exec, PTY, processes, files, git, browser) are served by the workspace’s cell at its cell_endpoint, with a token from POST /v1/workspaces/{workspace_id}/tool-tokens; they are not listed here.
Principal
The API key making the request: its organization, project and tool permissions.
GET /v1/me
The authenticated principal.
Authentication: API key
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
Organizations
The organization an API key belongs to.
GET /v1/organizations
Organizations visible to the principal.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
limit |
query | integer | no | |
cursor |
query | string | no |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/organizations/{organization_id}
Get an organization.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
Projects
The project an API key belongs to.
GET /v1/organizations/{organization_id}/projects
List projects.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
limit |
query | integer | no | |
cursor |
query | string | no | |
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/projects/{project_id}
Get a project.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
project_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
Workspaces
Open workspaces by key, and suspend, resume, snapshot, fork, reset or close them.
POST /v1/workspaces/open
Open a workspace by key (create on first use, reconnect or resume afterwards).
New keys resolve template to its latest published version; reopening never changes or resets the workspace (the response reports the template version actually used). 200 when the workspace is already running and ready (with cell_endpoint and a tool token); otherwise 202 with the operation to poll. A running workspace whose startup failed (startup.state failed) is not ready: the open is 202 with an open operation (input.startup_retry) that runs the failed step again. Concurrent opens of one key share one workspace and one operation. secrets (optional) binds secret names injected into every exec/PTY start: it sets the binding of a new key and replaces it on an existing key (omitted = unchanged); an unknown or unusable name is 422 details.reason secret_not_available with details.names (nothing is created or changed). The template version’s secret inputs join the binding on create and whenever secrets is given; a required one this workspace may not use is 422 input_required (details.kind secret); a bound name equal to a template env key or text input is 422 env_collision (details.name). inputs (optional): the version’s text inputs {NAME: string}; stored on create (else the declared default) and replaced on an existing key (omitted = unchanged); 422 input_unknown, input_invalid or input_required (details.names). lifetime: omitted = the version’s default (else persistent); session workspaces are discarded when the session ends (close(), idle timeout), after which the key opens a NEW workspace; reopening a live key with another lifetime is 409 lifetime_mismatch. A new workspace is layered when its version supports it and layered opens are enabled (disk_layout). Errors: 404 template/key outside scope, 402 entitlement_required, 403 quota_exceeded (details.limit), 409 operation in progress, deleted key (workspace_deleted) or lifetime_mismatch, 422 reserved_key_prefix (keys starting with sf:). Supports Idempotency-Key. Held open: with Prefer: wait=<seconds> (at most 20) an open whose outcome is an operation is held until the operation is terminal or the wait elapses; success answers 200 with the running workspace, the succeeded operation and a tool token (Preference-Applied: wait=<seconds>), anything else 202 with the fresh operation. Without Preference-Applied the server did not wait: poll the operation. mode: omitted = processful for a new key and the stored mode for an existing one; file_first creates a workspace whose state is a versioned file tree with no VM between executions: it is ready at once (200 with a tool token, observed_state running, tree_revision 0, no operation), needs a layered template version (409 layout_unsupported otherwise) and is persistent (lifetime: session or idle_policy with it are 422 not_supported_for_mode); each open of a file-first key re-resolves the size of its execution VMs from caps, the template and the plan. 422 mode_not_available while the deployment does not offer file-first workspaces; reopening a key with another mode is 409 mode_mismatch.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
prefer |
header | string | no | RFC 7240 preference, e.g. wait=20 (held open). |
Request body (application/json, required)
| Field | Type | Required | Description |
|---|---|---|---|
key |
string | yes | Stable workspace key, unique per organization (e.g. ${customerId}/${projectId}). |
template |
string | yes | Template slug; new workspaces use its latest published version. |
caps |
object | no | Optional user caps; the ceiling is min(template, cap, plan). Absent fields add no restriction. |
project_id |
string (uuid) | no | UUIDv7, lowercase canonical form. |
agent_label |
string | no | Attribution label; one agent session per (workspace, principal, label). |
tools |
array of "exec" or "files" or "pty" or "process" or "git" or "browser" | no | |
secrets |
array of string | no | Secret names bound to the workspace (max 50, unique): injected as environment variables into every exec and PTY start (terminal sessions included), together with the call’s own secret_refs. Each must name a live secret this workspace may use (its project, its id, and allowed_tools including exec and pty), else 422 details.reason secret_not_available with details.names. |
inputs |
object | no | Open-time inputs of the template version: {NAME: string} for its declared text inputs. A new workspace stores each given value, else the declared default; on an existing key inputs replaces them all (omitted = unchanged). Secret inputs are not passed here: they bind the stored secret of the same name. 422 input_unknown (undeclared name, details.names), input_invalid (a secret input, a non-string, or a value over 4096 bytes or with CR, LF or NUL; details.names), input_required (details {names, kind}). |
lifetime |
WorkspaceLifetime | no | persistent: kept until deleted (default). session: discarded when the session ends (close(), idle timeout or draft discard;). |
idle_policy |
string | no | adaptive (the learned timeout, default), never, or fixed:<seconds> (60..604800). |
mode |
WorkspaceMode | no | processful (default): one VM keeps processes, memory and files between calls; it can be suspended, resumed and forked. file_first: the state is a versioned file tree (tree_revision); there is no VM between executions, each exec runs in a fresh VM and publishes the changed files as the next revision, nothing but files survives an execution. A file-first workspace is ready (running) from creation and is never suspended; suspend, resume, snapshot, fork, reset, save-as-template, volumes and idle policies are 409 not_supported_for_mode. Immutable. |
Responses: 200 · 202 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/workspaces
List workspaces (API keys: their project; users: their organizations).
By default only persistent standard workspaces; lifetime and purpose (each also any) show sessions, drafts and test instances. Ended sessions are tombstones: add include_deleted=true.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
limit |
query | integer | no | |
cursor |
query | string | no | |
organization_id |
query | string (uuid) | no | UUIDv7, lowercase canonical form. |
project_id |
query | string (uuid) | no | UUIDv7, lowercase canonical form. |
state |
query | string (one of 11) | no | Observed state filter. |
desired_state |
query | "running" or "suspended" or "deleted" | no | |
key_prefix |
query | string | no | |
include_deleted |
query | boolean | no | |
lifetime |
query | "persistent" or "session" or "any" | no | persistent (default), session or any. Key lookups pass any. |
purpose |
query | "standard" or "template_draft" or "template_test" or "any" | no | standard (default), template_draft, template_test or any. Key lookups pass any. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/organizations/{organization_id}/workspaces
List an organization’s workspaces (API keys see only their project).
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
limit |
query | integer | no | |
cursor |
query | string | no | |
project_id |
query | string (uuid) | no | UUIDv7, lowercase canonical form. |
state |
query | string (one of 11) | no | Observed state filter. |
desired_state |
query | "running" or "suspended" or "deleted" | no | |
key_prefix |
query | string | no | |
include_deleted |
query | boolean | no | |
lifetime |
query | "persistent" or "session" or "any" | no | persistent (default), session or any. Key lookups pass any. |
purpose |
query | "standard" or "template_draft" or "template_test" or "any" | no | standard (default), template_draft, template_test or any. Key lookups pass any. |
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/workspaces/{workspace_id}
Get a workspace: desired/observed state, cell, template version, caps/ceilings, grants, active operation, pending reason.
Tombstoned workspaces stay readable (deleted_at set) until final cleanup.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspace_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 Workspace · 4XX ErrorBody · 5XX ErrorBody
DELETE /v1/workspaces/{workspace_id}
Delete a workspace (tombstone now, storage cleanup by the cell).
Sets desired_state=deleted and deleted_at, revokes tool access immediately (workspace revocation watermark, agent sessions revoked) and creates a delete operation for the cell. A file-first workspace’s tree revisions are deleted with the tombstone and the cell deletes its stored files. Repeating returns the same operation. The key is never reused. Supports Idempotency-Key.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspace_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 202 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/workspaces/{workspace_id}/inputs
The workspace’s text inputs.
The text inputs the workspace was opened with (the given value, else the declared default). Every exec, PTY, start command and service gets them as environment variables, above the template env and below the call’s own env. Secret inputs are bound secrets (GET …/secrets). Set on create; replaced by an open of the key with inputs. Deleted workspaces stay readable.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspace_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 WorkspaceInputs · 4XX ErrorBody · 5XX ErrorBody
PUT /v1/workspaces/{workspace_id}/idle-policy
Set or clear the workspace idle policy (automatic suspend).
idle_policy: adaptive (the learned timeout), never, or fixed:<seconds> (60..604800); null clears it so the template default (else adaptive) applies. Work signals always win: a running command, an attached session or a keepalive keeps the workspace running. Applies from the idle loop’s next evaluation. 409 session_lifetime for session workspaces (they end after their idle timeout), 409 not_supported_for_mode for file-first workspaces (never suspended), 409 workspace_deleted. Returns the workspace.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspace_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Request body (application/json, required)
| Field | Type | Required | Description |
|---|---|---|---|
idle_policy |
string or null | yes |
Responses: 200 Workspace · 4XX ErrorBody · 5XX ErrorBody
POST /v1/workspaces/{workspace_id}/suspend
Suspend a running workspace (durable full-state checkpoint; a session workspace is 409 session_lifetime).
Creates a durable operation executed by the cell (Phase 8); poll GET /operations/{id}. A file-first workspace is 409 not_supported_for_mode. Supports Idempotency-Key.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspace_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 202 · 4XX ErrorBody · 5XX ErrorBody
POST /v1/workspaces/{workspace_id}/resume
Resume a suspended workspace (admitted like a start).
Creates a resume operation executed by the cell, or returns the active resume/open (concurrent wakes join one operation); 202 with it: poll GET /operations/{id}. Errors: 409 already_running or not_suspended, operation_in_progress (a suspend or another operation is active; details.active_operation_id), workspace_deleted, not_supported_for_mode (file-first); 402/403 as for open (admitted like a start). Supports Idempotency-Key. Held resume: with Prefer: wait=<seconds> (at most 20) the response is held until the operation is terminal or the wait elapses, exactly like a held open; success answers 200 with the running workspace, the succeeded operation, cell_endpoint and a tool token for agent_label/tools (minted while the restore is in flight, at the new ownership epoch), Preference-Applied: wait=<seconds>; anything else is 202 with the fresh operation. With the preference a workspace that is already running answers 200 at once (operation null, a token) instead of 409 already_running. Without Preference-Applied the server did not wait: poll the operation. tools beyond the principal’s tool permissions are 403 (nothing is created); a token the API cannot issue otherwise is tool_token: null.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspace_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
prefer |
header | string | no | RFC 7240 preference, e.g. wait=20 (held resume). |
Request body (application/json, optional)
| Field | Type | Required | Description |
|---|---|---|---|
agent_label |
string | no | Attribution label; one agent session per (workspace, principal, label). |
tools |
array of "exec" or "files" or "pty" or "process" or "git" or "browser" | no |
Responses: 200 · 202 · 4XX ErrorBody · 5XX ErrorBody
POST /v1/workspaces/{workspace_id}/snapshot
Snapshot a running or suspended workspace.
Creates a durable operation executed by the cell (Phase 8); poll GET /operations/{id}. A file-first workspace is 409 not_supported_for_mode. Supports Idempotency-Key.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspace_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Request body (application/json, required)
| Field | Type | Required | Description |
|---|---|---|---|
label |
string | no |
Responses: 202 · 4XX ErrorBody · 5XX ErrorBody
POST /v1/workspaces/{workspace_id}/suspend-when-idle
Suspend the workspace if it stays idle for after_seconds from now (e.g. at the end of an agent turn).
Records a deferred suspend for the cell’s idle loop: once the workspace has been idle for after_seconds (30..3600), it is suspended (within a few seconds of activity flush grace), at not_before (= now + after_seconds) at the earliest. A tool call after the request (the next turn) or a resume cancels it for good. Other work only defers it: a command still running, an attached exec/PTY stream or a keepalive postpones the suspend until after_seconds after it ends. It applies under every idle policy (never included) and only ever shortens the policy’s wait; a policy that suspends sooner still does. Repeating replaces the pending request (the new requested_at counts); DELETE cancels it; idle.suspend_request on the workspace shows it. 202 {workspace, operation: null, suspend_request}; when a suspend is already in progress, 202 {workspace, operation: that suspend, suspend_request: null} and nothing is recorded. The suspend, when it happens, is a system suspend operation with input.reason requested_after_idle (input.requested_at, input.after_seconds). Errors: 409 session_lifetime, workspace_deleted, operation_in_progress (another lifecycle operation is active), not_running, not_supported_for_mode (file-first). Supports Idempotency-Key.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspace_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Request body (application/json, required)
| Field | Type | Required | Description |
|---|---|---|---|
after_seconds |
integer | yes | Seconds from now the workspace must stay idle before it is suspended (30..3600). |
Responses: 202 · 4XX ErrorBody · 5XX ErrorBody
DELETE /v1/workspaces/{workspace_id}/suspend-when-idle
Cancel a pending suspend-when-idle request.
Idempotent: 200 with the workspace whether or not a request was pending, in any workspace state (idle.suspend_request is null afterwards). A suspend the request already started is not undone: it shows as the workspace’s active_operation; resume or open the workspace instead.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspace_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 Workspace · 4XX ErrorBody · 5XX ErrorBody
POST /v1/workspaces/{workspace_id}/fork
Fork a workspace into a new key (independent copy of its committed state).
Creates the target workspace (same template version and disk layout) and a fork operation on it, admitted like a start. Caps default to the source’s. lifetime is the fork’s own (default persistent): forking a session keeps its state. A file-first source is 409 not_supported_for_mode. Supports Idempotency-Key.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspace_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Request body (application/json, required)
| Field | Type | Required | Description |
|---|---|---|---|
key |
string | yes | Stable workspace key, unique per organization (e.g. ${customerId}/${projectId}). |
caps |
object | no | Optional user caps; the ceiling is min(template, cap, plan). Absent fields add no restriction. |
lifetime |
WorkspaceLifetime | no | persistent: kept until deleted (default). session: discarded when the session ends (close(), idle timeout or draft discard;). |
Responses: 202 · 4XX ErrorBody · 5XX ErrorBody
POST /v1/workspaces/{workspace_id}/close
Close a session workspace (ends the session: the workspace is deleted).
Sessions only (409 not_session for a persistent workspace; the SDK then only closes local streams). Ends the session exactly like a delete (VM stopped without a snapshot, layer and checkpoints released) with ended_reason closed; the key then opens a NEW workspace. 202 with the delete operation (input.reason session_closed). Idempotent: repeating returns the active or last delete operation. Supports Idempotency-Key.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspace_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 202 · 4XX ErrorBody · 5XX ErrorBody
POST /v1/workspaces/{workspace_id}/reset
Reset a layered workspace to its template (wipes the workspace layer).
Keeps the key, id, template version, caps, secret bindings, volume attachments and history; wipes every change. Running: restarted on a blank layer (processes are gone, epoch + 1, old tool tokens get 409 stale_epoch). Suspended: stays suspended; the next resume boots blank. The previous checkpoint stays restorable for 7 days (result.recovery_checkpoint_id). 202 with the reset operation. Errors: 422 confirm_destructive_required, 409 legacy_disk_layout, not_resettable, operation_in_progress, not_supported_for_mode (file-first). Supports Idempotency-Key.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspace_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Request body (application/json, required, ResetWorkspaceBody)
| Field | Type | Required | Description |
|---|---|---|---|
confirm_destructive |
boolean | no | Must be true: reset wipes every change in the workspace layer (422 confirm_destructive_required otherwise). |
Responses: 202 · 4XX ErrorBody · 5XX ErrorBody
POST /v1/workspaces/{workspace_id}/save-as-template
Save a layered workspace as the next version of an organization template.
Everything in the workspace becomes template content (its whole filesystem, minus the sf-scrub.v1 list, the contents of /proc, /sys, /dev, /run and /tmp, and shared-volume contents), stored as one new org layer on the workspace’s template chain. A running workspace is captured briefly (operation, layer_snapshot); a suspended one uses its current checkpoint; checkpoint_id saves a committed checkpoint instead. 202 SaveAsTemplateResponse: poll the build. Owners/admins and API keys with a tool permission (403 otherwise). Errors: 409 legacy_disk_layout, workspace_not_running, operation_in_progress, not_supported_for_mode (file-first), template_archived, 422 platform_template_slug, invalid_defaults, update_policy_not_available, too_many_acknowledged_findings, invalid_path, 403 quota_exceeded (concurrent_template_builds). Supports Idempotency-Key.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspace_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Request body (application/json, required, SaveAsTemplateBody)
| Field | Type | Required | Description |
|---|---|---|---|
template_slug |
string | yes | Organization template to save into (created when absent; platform slugs are refused). |
display_name |
string | no | Template name when this save creates the template. |
description |
string | no | The version description (default: the source version’s). |
defaults |
object | no | Defaults of the new version (omitted fields: persistent lifetime, platform idle timeout, no limits). |
checkpoint_id |
string (uuid) | no | A committed checkpoint of this workspace to save instead of its current state. |
auto_publish |
boolean | no | |
acknowledged_scan_findings |
array of string | no | Up to 200 absolute paths the credential scan may report without failing the build (recorded in the manifest). |
settings |
TemplateSettingsInput | no | What a workspace of the version gets when it opens (). Omitted fields are empty (recipes do not carry settings forward). |
Responses: 202 SaveAsTemplateResponse · 4XX ErrorBody · 5XX ErrorBody
GET /v1/workspaces/{workspace_id}/secrets
Secret names bound to a workspace, with per-name status (never values).
Bound secrets are injected into every exec and PTY start of the workspace together with the call’s secret_refs. status: available | not_allowed | deleted. Deleted workspaces stay readable.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspace_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
PUT /v1/workspaces/{workspace_id}/secrets
Replace the secret names bound to a workspace.
Replaces the whole binding (names: [] clears it); applies from the next exec/PTY start (running processes keep their environment). Every name must be a live secret this workspace may use (its project, its id, and allowed_tools including exec and pty), else 422 details.reason secret_not_available with details.names and nothing changes. A name equal to a key of the template version’s env or one of its text inputs is 422 env_collision (details.name;). Exactly the given names are bound (the version’s secret inputs join the binding only through open). Audited when the binding changes. 409 workspace_deleted.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspace_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Request body (application/json, required)
| Field | Type | Required | Description |
|---|---|---|---|
names |
array of string | yes | Secret names bound to the workspace (max 50, unique): injected as environment variables into every exec and PTY start (terminal sessions included), together with the call’s own secret_refs. Each must name a live secret this workspace may use (its project, its id, and allowed_tools including exec and pty), else 422 details.reason secret_not_available with details.names. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
Operations
Lifecycle operations (open, suspend, resume, fork, ...) and their progress.
GET /v1/operations/{operation_id}
Poll an asynchronous operation.
Stays readable after its workspace is tombstoned. Bounded wait: with Prefer: wait=<seconds> (at most 20) and an operation that is not terminal, the response is held until its state or state_reason changes or the wait elapses, then carries the fresh operation and Preference-Applied: wait=<seconds>. Without Preference-Applied the server did not wait: poll with backoff.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
operation_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
prefer |
header | string | no | RFC 7240 preference, e.g. wait=20. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/workspaces/{workspace_id}/operations
List a workspace’s operations, newest first.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
limit |
query | integer | no | |
cursor |
query | string | no | |
state |
query | "queued" or "capacity_pending" or "running" or "succeeded" or "failed" or "canceled" | no | |
kind |
query | string (one of 15) | no | |
workspace_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
Tool tokens
Short-lived tokens for the workspace tools (exec, PTY, files, git, browser) served at the workspace’s cell_endpoint, and agent sessions.
POST /v1/workspaces/{workspace_id}/tool-tokens
Issue a workspace tool token (ES256, <= 15 min) for the cell gateway.
Creates or reuses the agent session for (workspace, principal, agent_label) and signs a token with claims iss, aud, sub, pty, org, prj, ws, epoch, tools, sid, iat, exp, jti (verify with GET /v1/.well-known/tool-token-keys). tools must be a subset of the API key’s tool permissions (users: of their role). A suspended workspace gets a token too: the cell serves it only the reads of its disk (files read, stat, list and search, X-Served-From: disk) and answers every other call 409 workspace_not_running (wake the workspace; its resume moves the ownership epoch). 409 with the active operation when the workspace is neither running nor suspended; on 409 stale_epoch from the cell, request a new token.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspace_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Request body (application/json, required)
| Field | Type | Required | Description |
|---|---|---|---|
agent_label |
string | no | Attribution label; one agent session per (workspace, principal, label). |
tools |
array of "exec" or "files" or "pty" or "process" or "git" or "browser" | no |
Responses: 201 ToolToken · 4XX ErrorBody · 5XX ErrorBody
GET /v1/workspaces/{workspace_id}/agent-sessions
List the attributed agent sessions of a workspace.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
limit |
query | integer | no | |
cursor |
query | string | no | |
workspace_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
Volumes
Persistent volumes shared between workspaces of a project.
GET /v1/projects/{project_id}/volumes
List a project’s shared volumes (optionally with the organization’s org-shared volumes).
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
limit |
query | integer | no | |
cursor |
query | string | no | |
include_org_shared |
query | boolean | no | Also list org-shared volumes owned by other projects of the organization (attachable here). |
include_deleted |
query | boolean | no | |
project_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
POST /v1/projects/{project_id}/volumes
Create a shared volume (EFS access point created by the cell).
202 with the volume (state creating) and its volume_create operation; the volume becomes available when the cell has created its storage (poll the operation or the volume). Quotas from the plan: limit.shared_volumes_max (live volumes per organization) and limit.shared_volume_gib_max (quota_gib of one volume) -> 403 quota_exceeded (details.limit); a plan without them -> 402 entitlement_required (details.reason limits_missing). org_shared: true (owners/admins only) makes it attachable from every project of the organization. 409 name_in_use. Supports Idempotency-Key.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
project_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Request body (application/json, required)
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | Unique among the project’s live volumes. |
quota_gib |
integer | yes | |
org_shared |
boolean | no |
Responses: 202 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/projects/{project_id}/volumes/{volume_id}
Get a shared volume (the project’s own, or an org-shared volume of its organization).
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
project_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
volume_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 Volume · 4XX ErrorBody · 5XX ErrorBody
DELETE /v1/projects/{project_id}/volumes/{volume_id}
Delete a shared volume and its data (executed by the cell).
202 with the volume (state deleting) and its volume_delete operation; deleted once the cell removed the data and the access point. Refused with 409 volume_attached (details.attachments) while it is attached, unless force=true&confirm_name=<volume name>: attachments are then detached by the delete (running guests see I/O errors on the mount). Only the owning project; org-shared volumes only by owners/admins. Idempotent (a deleting/deleted volume returns its delete operation). Supports Idempotency-Key.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
force |
query | boolean | no | |
confirm_name |
query | string | no | |
project_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
volume_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 202 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/projects/{project_id}/volumes/{volume_id}/attachments
List a volume’s attachments (API keys: only to their project’s workspaces).
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
limit |
query | integer | no | |
cursor |
query | string | no | |
include_detached |
query | boolean | no | |
project_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
volume_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/workspaces/{workspace_id}/volumes
List the volumes attached to a workspace.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
limit |
query | integer | no | |
cursor |
query | string | no | |
include_detached |
query | boolean | no | |
workspace_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
POST /v1/workspaces/{workspace_id}/volumes
Attach a shared volume to a workspace at mount_path (ro or rw).
202 with the attachment (state attaching) and its volume_attach operation; the cell mounts it in the running guest (or records it for the next start when the workspace has no VM) and reports attached. The volume must be available, of the same organization and of the workspace’s project unless org-shared (409 volume_not_shared). One attachment per (volume, workspace); mount paths may not nest (409 mount_path_conflict); at most 8 per workspace (403 quota_exceeded). Attach/detach share the workspace’s single active lifecycle operation: 409 operation_in_progress while another one runs. Re-attaching an attached volume with the same mount_path/mode returns 200 without a new operation. Supports Idempotency-Key.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspace_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Request body (application/json, required)
| Field | Type | Required | Description |
|---|---|---|---|
volume_id |
string (uuid) | yes | UUIDv7, lowercase canonical form. |
mount_path |
string | yes | Absolute guest path (e.g. /mnt/data); segments of A-Z a-z 0-9 . _ - not starting with "."; system directories (/etc, /usr, /proc, /tmp, /var, ...) and nesting with another mount are refused. |
mode |
"ro" or "rw" | no |
Responses: 200 · 202 · 4XX ErrorBody · 5XX ErrorBody
DELETE /v1/workspaces/{workspace_id}/volumes/{volume_id}
Detach a shared volume from a workspace.
202 with the attachment (state detaching) and its volume_detach operation; the cell unmounts it (the guest sees I/O errors on the path afterwards, never stale writes) and reports detached. Idempotent while detaching. 404 when the volume is not attached. 409 operation_in_progress while another lifecycle operation of the workspace runs. Supports Idempotency-Key.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspace_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
volume_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 202 · 4XX ErrorBody · 5XX ErrorBody
Templates
Published templates and their versions, files, recipes and diffs.
GET /v1/organizations/{organization_id}/templates
List the templates an organization can use (platform + its own), with the version open picks.
Archived templates only with include_archived=true. Ordered by id; cursor pagination.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
limit |
query | integer | no | |
cursor |
query | string | no | |
include_archived |
query | boolean | no | Include archived templates and versions. |
owner |
query | "platform" or "organization" | no | |
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/organizations/{organization_id}/templates/{slug}
Get a template by slug with its versions, compatibility, caps, installed tools and plan clamping.
The organization’s own template shadows a platform template of the same slug (like open); open_resolves_to is what open would use now. Unpublished versions appear only for owners/admins of the owning organization. 404 outside the organization.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
include_archived |
query | boolean | no | Include archived templates and versions. |
owner |
query | "platform" or "organization" | no | Pick the platform template even when an organization template shadows its slug. |
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
slug |
path | string | yes |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/templates
List the templates an organization can use (platform + its own), with the version open picks (the API key’s organization).
Archived templates only with include_archived=true. Ordered by id; cursor pagination.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
limit |
query | integer | no | |
cursor |
query | string | no | |
include_archived |
query | boolean | no | Include archived templates and versions. |
owner |
query | "platform" or "organization" | no |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/templates/{slug}
Get a template by slug with its versions, compatibility, caps, installed tools and plan clamping (the API key’s organization).
The organization’s own template shadows a platform template of the same slug (like open); open_resolves_to is what open would use now. Unpublished versions appear only for owners/admins of the owning organization. 404 outside the organization.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
include_archived |
query | boolean | no | Include archived templates and versions. |
owner |
query | "platform" or "organization" | no | Pick the platform template even when an organization template shadows its slug. |
slug |
path | string | yes |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/organizations/{organization_id}/templates/{slug}/versions/{version}/files
List one directory of a template version’s file tree.
Entries directly inside path (default /), sorted by name bytes, keyset-paginated. The tree covers the whole filesystem (only the contents of /proc, /sys, /dev, /run and /tmp are left out). 409 file_list_unavailable (no file list) or file_list_indexing (retryable); 404 path_not_found; 422 invalid_path.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
path |
query | string | no | Absolute path (/, /home/user, no trailing slash). |
limit |
query | integer | no | |
cursor |
query | string | no | |
owner |
query | "platform" or "organization" | no | Pick the platform template even when an organization template shadows its slug. |
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
slug |
path | string | yes | |
version |
path | integer | yes |
Responses: 200 TemplateFilePage · 4XX ErrorBody · 5XX ErrorBody
GET /v1/templates/{slug}/versions/{version}/files
List one directory of a template version’s file tree.
Entries directly inside path (default /), sorted by name bytes, keyset-paginated. The tree covers the whole filesystem (only the contents of /proc, /sys, /dev, /run and /tmp are left out). 409 file_list_unavailable (no file list) or file_list_indexing (retryable); 404 path_not_found; 422 invalid_path.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
path |
query | string | no | Absolute path (/, /home/user, no trailing slash). |
limit |
query | integer | no | |
cursor |
query | string | no | |
owner |
query | "platform" or "organization" | no | Pick the platform template even when an organization template shadows its slug. |
slug |
path | string | yes | |
version |
path | integer | yes |
Responses: 200 TemplateFilePage · 4XX ErrorBody · 5XX ErrorBody
GET /v1/organizations/{organization_id}/templates/{slug}/versions/{version}/files/entry
Get one entry of a template version’s file tree.
404 path_not_found; 409 file_list_unavailable / file_list_indexing; 422 invalid_path.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
path |
query | string | yes | Absolute path (/, /home/user, no trailing slash). |
owner |
query | "platform" or "organization" | no | Pick the platform template even when an organization template shadows its slug. |
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
slug |
path | string | yes | |
version |
path | integer | yes |
Responses: 200 TemplateFileEntry · 4XX ErrorBody · 5XX ErrorBody
GET /v1/templates/{slug}/versions/{version}/files/entry
Get one entry of a template version’s file tree.
404 path_not_found; 409 file_list_unavailable / file_list_indexing; 422 invalid_path.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
path |
query | string | yes | Absolute path (/, /home/user, no trailing slash). |
owner |
query | "platform" or "organization" | no | Pick the platform template even when an organization template shadows its slug. |
slug |
path | string | yes | |
version |
path | integer | yes |
Responses: 200 TemplateFileEntry · 4XX ErrorBody · 5XX ErrorBody
GET /v1/organizations/{organization_id}/templates/{slug}/diff
Diff two versions of a template (path, change, before, after).
from is a version number of this template or base (the to version’s build base, which may be a platform version). Keyset-paginated by path; path_prefix narrows it (string prefix), change filters one kind. The first page (no cursor) carries summary. 409 file_list_unavailable / file_list_indexing when either version has no loaded file list.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
from |
query | string | yes | A version number, or base. |
to |
query | integer | yes | |
path_prefix |
query | string | no | |
change |
query | "added" or "removed" or "changed" or "type_changed" or "metadata" | no | |
limit |
query | integer | no | |
cursor |
query | string | no | |
owner |
query | "platform" or "organization" | no | Pick the platform template even when an organization template shadows its slug. |
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
slug |
path | string | yes |
Responses: 200 TemplateDiffPage · 4XX ErrorBody · 5XX ErrorBody
GET /v1/templates/{slug}/diff
Diff two versions of a template (path, change, before, after).
from is a version number of this template or base (the to version’s build base, which may be a platform version). Keyset-paginated by path; path_prefix narrows it (string prefix), change filters one kind. The first page (no cursor) carries summary. 409 file_list_unavailable / file_list_indexing when either version has no loaded file list.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
from |
query | string | yes | A version number, or base. |
to |
query | integer | yes | |
path_prefix |
query | string | no | |
change |
query | "added" or "removed" or "changed" or "type_changed" or "metadata" | no | |
limit |
query | integer | no | |
cursor |
query | string | no | |
owner |
query | "platform" or "organization" | no | Pick the platform template even when an organization template shadows its slug. |
slug |
path | string | yes |
Responses: 200 TemplateDiffPage · 4XX ErrorBody · 5XX ErrorBody
GET /v1/organizations/{organization_id}/templates/{slug}/versions/{version}/recipe
Export the recipe and settings of a template version (request form, ready to build again).
recipe: v1 {base, dockerfile, network} or the recipe v2 document (base as <slug>@<version>, languages as {id, version}, files without size, settings as stored); building it again from the same base with the same API release, while its uploads exist, gives the same recipe_sha256. null for versions saved from a workspace or published by the platform. settings: the version’s TemplateSettings. Same visibility as the version (unpublished only for owners/admins of the owning organization).
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
owner |
query | "platform" or "organization" | no | Pick the platform template even when an organization template shadows its slug. |
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
slug |
path | string | yes | |
version |
path | integer | yes |
Responses: 200 TemplateVersionRecipe · 4XX ErrorBody · 5XX ErrorBody
GET /v1/templates/{slug}/versions/{version}/recipe
Export the recipe and settings of a template version (request form, ready to build again).
recipe: v1 {base, dockerfile, network} or the recipe v2 document (base as <slug>@<version>, languages as {id, version}, files without size, settings as stored); building it again from the same base with the same API release, while its uploads exist, gives the same recipe_sha256. null for versions saved from a workspace or published by the platform. settings: the version’s TemplateSettings. Same visibility as the version (unpublished only for owners/admins of the owning organization).
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
owner |
query | "platform" or "organization" | no | Pick the platform template even when an organization template shadows its slug. |
slug |
path | string | yes | |
version |
path | integer | yes |
Responses: 200 TemplateVersionRecipe · 4XX ErrorBody · 5XX ErrorBody
Template builds
Build template versions from a recipe or an uploaded context, and look up packages and languages.
GET /v1/organizations/{organization_id}/template-builds
List an organization’s template builds, newest first.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
limit |
query | integer | no | |
cursor |
query | string | no | |
state |
query | string (one of 8) | no | |
template |
query | string | no | Organization template slug. |
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
POST /v1/organizations/{organization_id}/template-builds
Request a custom template build (queued for the isolated builder).
202 with the queued build. Takes recipe v1 (a Dockerfile) or recipe v2 (: validated against the base, compiled to the builder’s steps, uploads locked). Records the canonical recipe and its SHA-256 (provenance input), pins the base version, clamps resources to the plan and assigns target_version. The cell executes it (building -> testing -> publishing -> published) and the API then registers the version (published at once unless auto_publish=false); poll GET .../template-builds/{id} until registration.state is registered (or the build failed/was canceled). builder_availability says whether a builder is running. Errors: 422 when the recipe is outside the host builder Dockerfile dialect or the slug is not a builder slug (details.reason: multi_stage_not_supported, from_not_template_base, stage_names_not_supported, from_flags_not_supported, base_mismatch, run_flags_not_supported, heredoc_not_supported, instruction_not_supported, no_build_context, env_invalid, user_invalid, too_many_steps, recipe_too_large, slug_not_supported_by_builder, platform_template_slug, reserved_template_slug, base_not_found, base_not_published, base_archived, architecture_not_supported, ...; line for line errors; recipe v2: invalid_recipe (details.field, details.detail), base_not_layered, language_unavailable, language_conflict, invalid_package, too_many_files, platform_owned_path, invalid_path, upload_required, upload_missing, upload_digest_mismatch, upload_too_large, too_many_steps, recipe_too_large, allowlist_empty, allow_hosts_without_allowlist, extra_hosts_without_auto, too_many_hosts, invalid_host, ip_literal_not_allowed, host_not_allowed, invalid_settings, services_unsupported, too_many_acknowledged_findings), 503 dependency_unavailable (uploads_not_configured), 402 entitlement_required, 403 quota_exceeded (concurrent_template_builds), 409 template_archived. Supports Idempotency-Key.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Request body (application/json, required)
| Field | Type | Required | Description |
|---|---|---|---|
template_slug |
string | yes | Organization template to build into (created by the first build). Platform slugs and the reserved slugs new and edit are refused for new templates. |
auto_publish |
boolean | no | Publish the produced version as soon as it is registered (new workspaces of the slug then use it). false: it stays unpublished until an owner/admin publishes it. |
display_name |
string | no | Template name when this build creates the template. |
recipe |
TemplateRecipeV1 or TemplateRecipeV2 | yes | Recipe v1 (TemplateRecipeV1: a Dockerfile, no schema field) or recipe v2 (TemplateRecipeV2, schema: "shardflux.template-recipe.v2": languages, packages, uploaded files, build steps, auto network and settings;). |
description |
string | no | The version description (manifest description). |
acknowledged_scan_findings |
array of string | no | Recipe v2 only: up to 200 absolute paths the credential scan may report without failing the build (recorded in the manifest). |
Responses: 202 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/organizations/{organization_id}/template-builds/{build_id}
Get a template build: state, timestamps, bounds, provenance, results, failure, log tail and builder availability.
Bounded wait: with Prefer: wait=<seconds> (at most 20) and a build that is not settled (settled = failed, canceled, legacy succeeded, or published with registration registered/failed), the response is held until state or registration.state changes or the wait elapses, then carries the fresh build and Preference-Applied: wait=<seconds>. Without Preference-Applied the server did not wait: poll with backoff.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
build_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
prefer |
header | string | no | RFC 7240 preference, e.g. wait=20. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
POST /v1/organizations/{organization_id}/template-builds/{build_id}/cancel
Cancel a template build.
queued -> canceled (200); building/testing/publishing -> cancellation requested (202; honoured until the artifact upload starts, so a build in publishing may still end published); canceled -> unchanged (200); published/succeeded/failed -> 409 build_finished. API keys may cancel only builds they requested.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
build_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 · 202 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/organizations/{organization_id}/template-builds/{build_id}/log-url
Get a short-lived download URL for the full build log.
Presigned S3 GET of builds/<build_id>.log (served as an attachment build-<id>.log, text/plain), valid until expires_at (at most 15 minutes). Same access as reading the build. 404 not_found with details.reason log_not_available (no full log recorded for this build, e.g. still running) or log_expired (past its retention); 503 dependency_unavailable when this deployment has no build-log bucket. Audited; never cached.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
build_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
POST /v1/organizations/{organization_id}/template-uploads
Request an upload of a recipe build input (a file, or a folder as an uncompressed tar), by content.
200 {upload, put: null} when the organization already has these bytes (a pending upload is checked in the bucket first). 201 with a presigned S3 PUT otherwise (valid 900 s): send the bytes with every header of put.headers (x-amz-checksum-sha256 and content-length are signed, so S3 refuses other bytes), then reference sha256:<hex> in the recipe’s build.files. Idempotent by content (no Idempotency-Key needed). Errors: 422 upload_too_large (details.limit upload_bytes_max: at most 5 GiB), 422 upload_digest_mismatch (the same sha256 is available with another size), 503 dependency_unavailable (uploads_not_configured). Owners/admins and API keys with a tool permission.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Request body (application/json, required, TemplateUploadRequest)
| Field | Type | Required | Description |
|---|---|---|---|
sha256 |
string | yes | Lower-case hex SHA-256 of the bytes. |
size |
integer | yes | Bytes, at most 5368709120 (5 GiB; more is 422 upload_too_large). |
kind |
"file" or "tar" | yes | file, or tar (an uncompressed ustar/pax archive of a folder). Recorded for display; the recipe entry decides. |
Responses: 200 TemplateUploadResponse · 201 TemplateUploadResponse · 4XX ErrorBody · 5XX ErrorBody
POST /v1/template-uploads
Request an upload of a recipe build input (a file, or a folder as an uncompressed tar), by content (the API key’s organization).
200 {upload, put: null} when the organization already has these bytes (a pending upload is checked in the bucket first). 201 with a presigned S3 PUT otherwise (valid 900 s): send the bytes with every header of put.headers (x-amz-checksum-sha256 and content-length are signed, so S3 refuses other bytes), then reference sha256:<hex> in the recipe’s build.files. Idempotent by content (no Idempotency-Key needed). Errors: 422 upload_too_large (details.limit upload_bytes_max: at most 5 GiB), 422 upload_digest_mismatch (the same sha256 is available with another size), 503 dependency_unavailable (uploads_not_configured). Owners/admins and API keys with a tool permission.
Authentication: API key
Request body (application/json, required, TemplateUploadRequest)
| Field | Type | Required | Description |
|---|---|---|---|
sha256 |
string | yes | Lower-case hex SHA-256 of the bytes. |
size |
integer | yes | Bytes, at most 5368709120 (5 GiB; more is 422 upload_too_large). |
kind |
"file" or "tar" | yes | file, or tar (an uncompressed ustar/pax archive of a folder). Recorded for display; the recipe entry decides. |
Responses: 200 TemplateUploadResponse · 201 TemplateUploadResponse · 4XX ErrorBody · 5XX ErrorBody
GET /v1/organizations/{organization_id}/template-packages
Search apt, pip or npm packages for a recipe.
apt: the base’s package index (409 package_index_unavailable when the base has none); pip: the daily PyPI name list (names only; 409 package_index_unavailable before its first refresh); npm: the registry search. Upstreams are bounded (503 dependency_unavailable, retryable, when they do not answer within 2.5 s) and each organization may make 120 lookups a minute (429 rate_limited). Owners/admins and API keys with a tool permission.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
ecosystem |
query | "apt" or "pip" or "npm" | yes | |
q |
query | string | yes | |
base |
query | string | no | <slug>@<version> (apt: the base whose apt index is searched). |
limit |
query | integer | no | |
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 TemplatePackagePage · 4XX ErrorBody · 5XX ErrorBody
GET /v1/organizations/{organization_id}/template-packages/{ecosystem}/{name}
Get one apt, pip or npm package: latest version, summary and versions.
404 not_found (package_not_found). apt needs base=<slug>@<version>. Same limits as the search.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
base |
query | string | no | <slug>@<version> (apt: the base whose apt index is searched). |
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
ecosystem |
path | "apt" or "pip" or "npm" | yes | |
name |
path | string | yes |
Responses: 200 TemplatePackage · 4XX ErrorBody · 5XX ErrorBody
GET /v1/template-packages
Search apt, pip or npm packages for a recipe (the API key’s organization).
apt: the base’s package index (409 package_index_unavailable when the base has none); pip: the daily PyPI name list (names only; 409 package_index_unavailable before its first refresh); npm: the registry search. Upstreams are bounded (503 dependency_unavailable, retryable, when they do not answer within 2.5 s) and each organization may make 120 lookups a minute (429 rate_limited). Owners/admins and API keys with a tool permission.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
ecosystem |
query | "apt" or "pip" or "npm" | yes | |
q |
query | string | yes | |
base |
query | string | no | <slug>@<version> (apt: the base whose apt index is searched). |
limit |
query | integer | no |
Responses: 200 TemplatePackagePage · 4XX ErrorBody · 5XX ErrorBody
GET /v1/template-packages/{ecosystem}/{name}
Get one apt, pip or npm package: latest version, summary and versions (the API key’s organization).
404 not_found (package_not_found). apt needs base=<slug>@<version>. Same limits as the search.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
base |
query | string | no | <slug>@<version> (apt: the base whose apt index is searched). |
ecosystem |
path | "apt" or "pip" or "npm" | yes | |
name |
path | string | yes |
Responses: 200 TemplatePackage · 4XX ErrorBody · 5XX ErrorBody
GET /v1/organizations/{organization_id}/template-languages
The recipe languages a base offers: version, default, whether the base already has it, and the hosts its install needs.
The language table read for the chain’s platform base of base, resolved as a build resolves recipe.base (422 validation_failed with details.field "base" and the build’s reasons: base_not_found, base_archived, base_not_published, architecture_not_supported). included: the base already has that version (python-node-browser: python and node), so the build installs nothing for it. A version the base has another version of is left out (a build would refuse it with language_conflict). Owners/admins and API keys with a tool permission.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
base |
query | string | yes | <slug>@<version> (apt: the base whose apt index is searched). |
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 TemplateLanguages · 4XX ErrorBody · 5XX ErrorBody
GET /v1/template-languages
The recipe languages a base offers: version, default, whether the base already has it, and the hosts its install needs (the API key’s organization).
The language table read for the chain’s platform base of base, resolved as a build resolves recipe.base (422 validation_failed with details.field "base" and the build’s reasons: base_not_found, base_archived, base_not_published, architecture_not_supported). included: the base already has that version (python-node-browser: python and node), so the build installs nothing for it. A version the base has another version of is left out (a build would refuse it with language_conflict). Owners/admins and API keys with a tool permission.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
base |
query | string | yes | <slug>@<version> (apt: the base whose apt index is searched). |
Responses: 200 TemplateLanguages · 4XX ErrorBody · 5XX ErrorBody
GET /v1/organizations/{organization_id}/template-builder-availability
Whether a template builder is running (heartbeat within 60 s).
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
Template drafts
Draft templates: iterate on states and test instances, then publish a version.
GET /v1/organizations/{organization_id}/templates/{slug}/draft
Get the template’s draft.
404 draft_not_found when the template has no live draft.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
slug |
path | string | yes |
Responses: 200 TemplateDraft · 4XX ErrorBody · 5XX ErrorBody
POST /v1/organizations/{organization_id}/templates/{slug}/draft
Create the template’s draft (a layered workspace on the draft base).
Opens the organization template’s single live draft (key sf:draft:<slug>:<8 hex>, purpose template_draft, persistent, layered) on base (default: the latest published version; required when the template has none, which creates the organization template named display_name, default the slug). 202 with the open operation and the draft. Errors: 409 template_not_layered (the base is not layered-capable, or layered opens are off), 409 draft_exists (details.workspace_id), 403 template_dev_mode_role, 422 platform_template_slug / reserved_template_slug (new, edit) / base_required. Supports Idempotency-Key.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
slug |
path | string | yes |
Request body (application/json, required, CreateDraftBody)
| Field | Type | Required | Description |
|---|---|---|---|
base |
string | no | <slug>@<version>: a published, layered-capable version (default: the template’s latest published version; required when the template has none, which creates the organization template). |
project_id |
string (uuid) | no | UUIDv7, lowercase canonical form. |
display_name |
string | no | Template name when this draft creates the organization template (default: the slug). |
caps |
object | no | |
agent_label |
string | no | |
tools |
array of "exec" or "files" or "pty" or "process" or "git" or "browser" | no | |
inputs |
object | no | Open-time inputs of the template version: {NAME: string} for its declared text inputs. A new workspace stores each given value, else the declared default; on an existing key inputs replaces them all (omitted = unchanged). Secret inputs are not passed here: they bind the stored secret of the same name. 422 input_unknown (undeclared name, details.names), input_invalid (a secret input, a non-string, or a value over 4096 bytes or with CR, LF or NUL; details.names), input_required (details {names, kind}). |
Responses: 200 · 202 · 4XX ErrorBody · 5XX ErrorBody
DELETE /v1/organizations/{organization_id}/templates/{slug}/draft
Discard the draft (delete it and end its live test instances).
Deletes the draft (operation delete, input.reason draft_discarded) and ends each live test instance (ended_reason draft_discarded). 202 with the draft’s delete operation. Supports Idempotency-Key.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
slug |
path | string | yes |
Responses: 202 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/templates/{slug}/draft
Get the template’s draft.
404 draft_not_found when the template has no live draft.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
slug |
path | string | yes |
Responses: 200 TemplateDraft · 4XX ErrorBody · 5XX ErrorBody
POST /v1/templates/{slug}/draft
Create the template’s draft (a layered workspace on the draft base).
Opens the organization template’s single live draft (key sf:draft:<slug>:<8 hex>, purpose template_draft, persistent, layered) on base (default: the latest published version; required when the template has none, which creates the organization template named display_name, default the slug). 202 with the open operation and the draft. Errors: 409 template_not_layered (the base is not layered-capable, or layered opens are off), 409 draft_exists (details.workspace_id), 403 template_dev_mode_role, 422 platform_template_slug / reserved_template_slug (new, edit) / base_required. Supports Idempotency-Key.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
slug |
path | string | yes |
Request body (application/json, required, CreateDraftBody)
| Field | Type | Required | Description |
|---|---|---|---|
base |
string | no | <slug>@<version>: a published, layered-capable version (default: the template’s latest published version; required when the template has none, which creates the organization template). |
project_id |
string (uuid) | no | UUIDv7, lowercase canonical form. |
display_name |
string | no | Template name when this draft creates the organization template (default: the slug). |
caps |
object | no | |
agent_label |
string | no | |
tools |
array of "exec" or "files" or "pty" or "process" or "git" or "browser" | no | |
inputs |
object | no | Open-time inputs of the template version: {NAME: string} for its declared text inputs. A new workspace stores each given value, else the declared default; on an existing key inputs replaces them all (omitted = unchanged). Secret inputs are not passed here: they bind the stored secret of the same name. 422 input_unknown (undeclared name, details.names), input_invalid (a secret input, a non-string, or a value over 4096 bytes or with CR, LF or NUL; details.names), input_required (details {names, kind}). |
Responses: 200 · 202 · 4XX ErrorBody · 5XX ErrorBody
DELETE /v1/templates/{slug}/draft
Discard the draft (delete it and end its live test instances).
Deletes the draft (operation delete, input.reason draft_discarded) and ends each live test instance (ended_reason draft_discarded). 202 with the draft’s delete operation. Supports Idempotency-Key.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
slug |
path | string | yes |
Responses: 202 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/organizations/{organization_id}/templates/{slug}/draft/states
List the draft’s states, newest first.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
limit |
query | integer | no | |
cursor |
query | string | no | |
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
slug |
path | string | yes |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
POST /v1/organizations/{organization_id}/templates/{slug}/draft/states
Capture a draft state (disk-only layer_snapshot of the running draft).
202 with the layer_snapshot operation; its result carries checkpoint_id. A suspended draft is 409 workspace_not_running (its current checkpoint already serves as a state). Supports Idempotency-Key.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
slug |
path | string | yes |
Request body (application/json, required)
| Field | Type | Required | Description |
|---|---|---|---|
label |
string | no |
Responses: 202 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/templates/{slug}/draft/states
List the draft’s states, newest first.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
limit |
query | integer | no | |
cursor |
query | string | no | |
slug |
path | string | yes |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
POST /v1/templates/{slug}/draft/states
Capture a draft state (disk-only layer_snapshot of the running draft).
202 with the layer_snapshot operation; its result carries checkpoint_id. A suspended draft is 409 workspace_not_running (its current checkpoint already serves as a state). Supports Idempotency-Key.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
slug |
path | string | yes |
Request body (application/json, required)
| Field | Type | Required | Description |
|---|---|---|---|
label |
string | no |
Responses: 202 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/organizations/{organization_id}/templates/{slug}/draft/test-instances
List the draft’s test instances.
Live instances; include_ended=true adds ended ones (tombstones with ended_reason). API keys see only their own project’s instances.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
limit |
query | integer | no | |
cursor |
query | string | no | |
include_ended |
query | boolean | no | |
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
slug |
path | string | yes |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
POST /v1/organizations/{organization_id}/templates/{slug}/draft/test-instances
Open a test instance of a draft state (a disposable session workspace).
Opens a layered session workspace (purpose template_test) on the draft base whose layer is a copy of the draft state; its own writes never reach the draft. Without state_id the running draft is captured first (the instance’s open depends on that layer_snapshot; origin.checkpoint_id is filled when the cell places it); a suspended draft’s current checkpoint is used instead. The instance ends on close(), idle or draft discard. 202 OpenResponse. Supports Idempotency-Key.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
slug |
path | string | yes |
Request body (application/json, required, CreateTestInstanceBody)
| Field | Type | Required | Description |
|---|---|---|---|
state_id |
string (uuid) | no | A draft state (GET …/draft/states). Omitted: a fresh capture of the running draft (its current checkpoint when suspended). |
key |
string | no | Workspace key (default sf:test:<slug>:<8 hex>); keys starting with sf: are reserved. |
caps |
object | no | |
agent_label |
string | no | |
tools |
array of "exec" or "files" or "pty" or "process" or "git" or "browser" | no | |
inputs |
object | no | Open-time inputs of the template version: {NAME: string} for its declared text inputs. A new workspace stores each given value, else the declared default; on an existing key inputs replaces them all (omitted = unchanged). Secret inputs are not passed here: they bind the stored secret of the same name. 422 input_unknown (undeclared name, details.names), input_invalid (a secret input, a non-string, or a value over 4096 bytes or with CR, LF or NUL; details.names), input_required (details {names, kind}). |
Responses: 200 · 202 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/templates/{slug}/draft/test-instances
List the draft’s test instances.
Live instances; include_ended=true adds ended ones (tombstones with ended_reason). API keys see only their own project’s instances.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
limit |
query | integer | no | |
cursor |
query | string | no | |
include_ended |
query | boolean | no | |
slug |
path | string | yes |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
POST /v1/templates/{slug}/draft/test-instances
Open a test instance of a draft state (a disposable session workspace).
Opens a layered session workspace (purpose template_test) on the draft base whose layer is a copy of the draft state; its own writes never reach the draft. Without state_id the running draft is captured first (the instance’s open depends on that layer_snapshot; origin.checkpoint_id is filled when the cell places it); a suspended draft’s current checkpoint is used instead. The instance ends on close(), idle or draft discard. 202 OpenResponse. Supports Idempotency-Key.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
slug |
path | string | yes |
Request body (application/json, required, CreateTestInstanceBody)
| Field | Type | Required | Description |
|---|---|---|---|
state_id |
string (uuid) | no | A draft state (GET …/draft/states). Omitted: a fresh capture of the running draft (its current checkpoint when suspended). |
key |
string | no | Workspace key (default sf:test:<slug>:<8 hex>); keys starting with sf: are reserved. |
caps |
object | no | |
agent_label |
string | no | |
tools |
array of "exec" or "files" or "pty" or "process" or "git" or "browser" | no | |
inputs |
object | no | Open-time inputs of the template version: {NAME: string} for its declared text inputs. A new workspace stores each given value, else the declared default; on an existing key inputs replaces them all (omitted = unchanged). Secret inputs are not passed here: they bind the stored secret of the same name. 422 input_unknown (undeclared name, details.names), input_invalid (a secret input, a non-string, or a value over 4096 bytes or with CR, LF or NUL; details.names), input_required (details {names, kind}). |
Responses: 200 · 202 · 4XX ErrorBody · 5XX ErrorBody
POST /v1/organizations/{organization_id}/templates/{slug}/draft/publish
Publish the draft as the template’s next version (save-as-template from the draft).
Builds one new org layer holding every change since the draft base, from state_id or the draft’s current state (a fresh capture when running). settings (TemplateSettingsInput): each given field replaces that field of the draft base’s settings, each absent one is carried forward (settings.defaults and defaults together: 422 invalid_settings). Refused with 409 draft_stale (details latest_version, draft_base_version) when the template has a version newer than the draft base that this draft did not produce, and 409 build_in_progress while a build from another source is unfinished. 202 SaveAsTemplateResponse. Supports Idempotency-Key.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
slug |
path | string | yes |
Request body (application/json, required, PublishDraftBody)
| Field | Type | Required | Description |
|---|---|---|---|
state_id |
string (uuid) | no | UUIDv7, lowercase canonical form. |
description |
string | no | |
defaults |
object | no | Defaults of the new version (omitted fields: persistent lifetime, platform idle timeout, no limits). |
settings |
TemplateSettingsInput | no | What a workspace of the version gets when it opens (). Omitted fields are empty (recipes do not carry settings forward). |
auto_publish |
boolean | no | |
acknowledged_scan_findings |
array of string | no | Up to 200 absolute paths the credential scan may report without failing the build (recorded in the manifest). |
Responses: 202 SaveAsTemplateResponse · 4XX ErrorBody · 5XX ErrorBody
POST /v1/templates/{slug}/draft/publish
Publish the draft as the template’s next version (save-as-template from the draft).
Builds one new org layer holding every change since the draft base, from state_id or the draft’s current state (a fresh capture when running). settings (TemplateSettingsInput): each given field replaces that field of the draft base’s settings, each absent one is carried forward (settings.defaults and defaults together: 422 invalid_settings). Refused with 409 draft_stale (details latest_version, draft_base_version) when the template has a version newer than the draft base that this draft did not produce, and 409 build_in_progress while a build from another source is unfinished. 202 SaveAsTemplateResponse. Supports Idempotency-Key.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
slug |
path | string | yes |
Request body (application/json, required, PublishDraftBody)
| Field | Type | Required | Description |
|---|---|---|---|
state_id |
string (uuid) | no | UUIDv7, lowercase canonical form. |
description |
string | no | |
defaults |
object | no | Defaults of the new version (omitted fields: persistent lifetime, platform idle timeout, no limits). |
settings |
TemplateSettingsInput | no | What a workspace of the version gets when it opens (). Omitted fields are empty (recipes do not carry settings forward). |
auto_publish |
boolean | no | |
acknowledged_scan_findings |
array of string | no | Up to 200 absolute paths the credential scan may report without failing the build (recorded in the manifest). |
Responses: 202 SaveAsTemplateResponse · 4XX ErrorBody · 5XX ErrorBody
POST /v1/organizations/{organization_id}/templates/{slug}/versions/{version}/test-instances
Open a test instance of a template version, published or not.
Opens a layered session workspace (purpose template_test) on that version of the organization’s template: registered and not archived, published or not. It is a fresh workspace (a create startup run) with origin {kind: template_version, template_id, version}; it ends on close() or idle. inputs as for open (422 input_unknown, input_invalid, input_required). 202 OpenResponse (200 when an existing key is running and ready). Errors: 403 template_dev_mode_role (owners, admins and API keys with a tool permission only), 404 version_not_found, 409 template_not_layered, template_archived, 422 reserved_key_prefix. Supports Idempotency-Key.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
slug |
path | string | yes | |
version |
path | integer | yes |
Request body (application/json, required, CreateVersionTestInstanceBody)
| Field | Type | Required | Description |
|---|---|---|---|
key |
string | no | Workspace key (default sf:test:<slug>:<8 hex>); caller keys starting with sf: are reserved. |
caps |
object | no | |
agent_label |
string | no | |
tools |
array of "exec" or "files" or "pty" or "process" or "git" or "browser" | no | |
inputs |
object | no | Open-time inputs of the template version: {NAME: string} for its declared text inputs. A new workspace stores each given value, else the declared default; on an existing key inputs replaces them all (omitted = unchanged). Secret inputs are not passed here: they bind the stored secret of the same name. 422 input_unknown (undeclared name, details.names), input_invalid (a secret input, a non-string, or a value over 4096 bytes or with CR, LF or NUL; details.names), input_required (details {names, kind}). |
project_id |
string (uuid) | no | UUIDv7, lowercase canonical form. |
Responses: 200 · 202 · 4XX ErrorBody · 5XX ErrorBody
POST /v1/templates/{slug}/versions/{version}/test-instances
Open a test instance of a template version, published or not.
Opens a layered session workspace (purpose template_test) on that version of the organization’s template: registered and not archived, published or not. It is a fresh workspace (a create startup run) with origin {kind: template_version, template_id, version}; it ends on close() or idle. inputs as for open (422 input_unknown, input_invalid, input_required). 202 OpenResponse (200 when an existing key is running and ready). Errors: 403 template_dev_mode_role (owners, admins and API keys with a tool permission only), 404 version_not_found, 409 template_not_layered, template_archived, 422 reserved_key_prefix. Supports Idempotency-Key.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
slug |
path | string | yes | |
version |
path | integer | yes |
Request body (application/json, required, CreateVersionTestInstanceBody)
| Field | Type | Required | Description |
|---|---|---|---|
key |
string | no | Workspace key (default sf:test:<slug>:<8 hex>); caller keys starting with sf: are reserved. |
caps |
object | no | |
agent_label |
string | no | |
tools |
array of "exec" or "files" or "pty" or "process" or "git" or "browser" | no | |
inputs |
object | no | Open-time inputs of the template version: {NAME: string} for its declared text inputs. A new workspace stores each given value, else the declared default; on an existing key inputs replaces them all (omitted = unchanged). Secret inputs are not passed here: they bind the stored secret of the same name. 422 input_unknown (undeclared name, details.names), input_invalid (a secret input, a non-string, or a value over 4096 bytes or with CR, LF or NUL; details.names), input_required (details {names, kind}). |
project_id |
string (uuid) | no | UUIDv7, lowercase canonical form. |
Responses: 200 · 202 · 4XX ErrorBody · 5XX ErrorBody
Secrets
Project secrets, their versions, and the secrets bound to a workspace.
GET /v1/projects/{project_id}/secrets
List a project’s secrets (metadata only).
Project-scoped secrets only; organization secrets are listed under the organization.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
limit |
query | integer | no | |
cursor |
query | string | no | |
include_deleted |
query | boolean | no | |
project_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
POST /v1/projects/{project_id}/secrets
Create a project secret (owner/admin, or an API key of this project).
The value is encrypted (KMS envelope) and never returned. allowed_tools defaults to [exec, pty]. 409 when the name exists in this project. Supports Idempotency-Key.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
project_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Request body (application/json, required)
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | Environment-variable-safe name, unique per scope (SHARDFLUX_ prefix reserved). |
description |
string | no | |
value |
string | yes | UTF-8 text, at most 65536 bytes, no NUL characters. Write-only: never returned by any management API. |
allowed_workspace_ids |
array of string (uuid) or null | no | Default null (any workspace of the project). |
allowed_tools |
array of "exec" or "files" or "pty" or "process" or "git" or "browser" | no |
Responses: 201 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/secrets/{secret_id}
Get secret metadata (never the value).
Deleted secrets stay readable (deleted_at set).
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
secret_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
PATCH /v1/secrets/{secret_id}
Update a secret’s description or usage permissions.
Takes effect for the next session start that resolves it. Names are immutable (delete and recreate).
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
secret_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Request body (application/json, required)
| Field | Type | Required | Description |
|---|---|---|---|
description |
string | no | |
allowed_project_ids |
array of string (uuid) or null | no | |
allowed_workspace_ids |
array of string (uuid) or null | no | |
allowed_tools |
array of "exec" or "files" or "pty" or "process" or "git" or "browser" | no |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
DELETE /v1/secrets/{secret_id}
Delete a secret (tombstone; erases every stored value; resolution stops immediately).
Any session start after this returns it as denied. The name becomes reusable. The name is removed from every workspace binding that referred to this secret (same transaction, one audit event per workspace). Values already injected into running processes cannot be recalled. Repeating returns 204.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
secret_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 204 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/secrets/{secret_id}/versions
List a secret’s versions, newest first (metadata only).
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
limit |
query | integer | no | |
cursor |
query | string | no | |
secret_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
POST /v1/secrets/{secret_id}/versions
Rotate: store a new value as the next version.
The new version is used from the next session start; every older version’s value is erased in the same transaction (metadata kept). Values already injected into running processes are not recalled. Supports Idempotency-Key.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
secret_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Request body (application/json, required)
| Field | Type | Required | Description |
|---|---|---|---|
value |
string | yes | UTF-8 text, at most 65536 bytes, no NUL characters. Write-only: never returned by any management API. |
Responses: 201 · 4XX ErrorBody · 5XX ErrorBody
Egress policy
Outbound network policy of a project or a workspace.
GET /v1/projects/{project_id}/egress-policy
Get a project’s egress policy (default for its workspaces).
version (also the ETag) is the value to send as If-Match. Without a project policy the effective policy is the platform default (allow_all).
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
project_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
PUT /v1/projects/{project_id}/egress-policy
Replace a project’s egress policy (creates a new immutable version).
mode allow_all | allowlist | deny_all; rules/cidrs only with allowlist. Hosts: exact FQDN or *. single-label wildcard (IDNA-normalized, lower-case); IP literals and localhost-like names are rejected. Ports 1-65535 (default [443]); protocols tcp only. cidrs: public ranges only. Optional If-Match: <version> (409 conflict with details.reason version_mismatch). Owner/admin or an API key of this project with tool permissions.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
project_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
if-match |
header | string | no |
Request body (application/json, required)
| Field | Type | Required | Description |
|---|---|---|---|
mode |
"allow_all" or "allowlist" or "deny_all" | yes | |
rules |
array of object | no | allowlist only; at most 256. |
cidrs |
array of string | no | allowlist only; public ranges; at most 64. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/projects/{project_id}/egress-policy/versions
Version history of a project’s egress policy, newest first.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
limit |
query | integer | no | |
cursor |
query | string | no | |
project_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/workspaces/{workspace_id}/egress-policy
Effective egress policy of a workspace and its enforcement state.
effective = workspace override > project policy > platform default (allow_all). enforcement.state is not_enforced until the cell/host acknowledges the current effective policy for the workspace’s current ownership epoch, then pending | enforced | failed. version (ETag) is the override history version for If-Match. template_egress: the template version’s egress ceiling (, null = none); effective_policy: what the host enforces, effective intersected with that ceiling.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspace_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
PUT /v1/workspaces/{workspace_id}/egress-policy
Set a workspace override of the egress policy (new immutable version).
Same body and validation as the project policy. Optional If-Match. 409 conflict (details.reason workspace_deleted) for deleted workspaces. 422 egress_widening (details {template_egress, outside: [hosts] | ["cidrs"] | ["allow_all"]}) when the workspace’s template egress ceiling would narrow the policy.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspace_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
if-match |
header | string | no |
Request body (application/json, required)
| Field | Type | Required | Description |
|---|---|---|---|
mode |
"allow_all" or "allowlist" or "deny_all" | yes | |
rules |
array of object | no | allowlist only; at most 256. |
cidrs |
array of string | no | allowlist only; public ranges; at most 64. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
DELETE /v1/workspaces/{workspace_id}/egress-policy
Remove the workspace override (fall back to the project policy).
Records a cleared version. 204 without a new version when there is no active override. Optional If-Match.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspace_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
if-match |
header | string | no |
Responses: 204 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/workspaces/{workspace_id}/egress-policy/versions
Version history of a workspace’s egress override (including cleared versions), newest first.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
limit |
query | integer | no | |
cursor |
query | string | no | |
workspace_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
Usage and spend
Metered usage, estimates, grants and spend of an organization or a workspace.
GET /v1/organizations/{organization_id}/usage/summary
Current-period usage, included allowances and cap state of an organization.
Totals come from the usage ledger (hourly, finalized after the lateness window); measurement.measured_through says how far usage is complete. Billable units are whole units (floor per workspace-hour); allowance usage counts billable units. allowance_exhausted true means new opens/resumes answer 402 allowance_exhausted with exhausted_reason as details.reason. spend_cap is opt-in overage this period: while it is on, a CPU-hours or RAM GiB-hours allowance past included is in cap state overage (starts admitted, usage past it charged) until the spend cap is reached.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/organizations/{organization_id}/usage
Usage time series (hour or day buckets) from the ledger.
Defaults to the current period at day granularity; hour granularity covers at most 31 days, day at most 400. workspace_id / volume_id (mutually exclusive) narrow it to one workspace or one shared volume (meter volume_storage_gib_seconds). API keys see only their own project.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
from |
query | string (date-time) | no | RFC 3339 UTC timestamp with Z. |
to |
query | string (date-time) | no | RFC 3339 UTC timestamp with Z. |
granularity |
query | "hour" or "day" | no | |
meter |
query | "cpu_seconds" or "memory_gib_seconds" or "storage_gib_seconds" or "egress_bytes" or "ingress_bytes" or "volume_storage_gib_seconds" | no | |
workspace_id |
query | string (uuid) | no | UUIDv7, lowercase canonical form. |
volume_id |
query | string (uuid) | no | UUIDv7, lowercase canonical form. |
project_id |
query | string (uuid) | no | UUIDv7, lowercase canonical form. |
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/workspaces/{workspace_id}/usage
Usage of one workspace (time series and totals).
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
from |
query | string (date-time) | no | RFC 3339 UTC timestamp with Z. |
to |
query | string (date-time) | no | RFC 3339 UTC timestamp with Z. |
granularity |
query | "hour" or "day" | no | |
meter |
query | "cpu_seconds" or "memory_gib_seconds" or "storage_gib_seconds" or "egress_bytes" or "ingress_bytes" or "volume_storage_gib_seconds" | no | |
workspace_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/organizations/{organization_id}/usage/estimate
Period cost estimate: subscription fee, usage charges and projected allowance use.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/organizations/{organization_id}/grants
Quotas, per-workspace ceilings/reservations/grants and compute budget leases.
Ceiling = min(template, user cap, plan cap). Reservations are organization-level (pending starts count). Leases are the compute budgets granted to the cell (<= 15 min). API keys see only their own project’s workspaces.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
limit |
query | integer | no | |
cursor |
query | string | no | |
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/organizations/{organization_id}/spend
Spend policy, charges this period, cap state and enforcement (leases, overshoot bound).
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/organizations/{organization_id}/spend-policy
Usage alert thresholds and opt-in overage with its spend cap.
Alert thresholds, and whether overage is available, on, off or paused, with the spend cap, its bounds and the rates. Overage is off by default; owners and billing members change it with the PUT (the console on /api/v1, or a CLI session on /v1; project API keys are refused).
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
Entitlements
The limits and features the organization’s plan grants.
GET /v1/organizations/{organization_id}/entitlements
Resolved plan limits, allowances and policies of an organization.
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
Billing
The plan catalog and the organization’s subscription.
GET /v1/billing/catalog
The active plan catalog (the same versioned catalog admission enforces).
Public, cacheable. Prices are in minor units; purchasable plans can be bought with POST .../organizations/{id}/billing/checkout-sessions (a browser or CLI session; shard billing upgrade <plan>).
Authentication: None (public)
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
GET /v1/organizations/{organization_id}/billing/subscription
Subscription, billing state and effective plan of an organization.
Owner/billing members (browser) or any API key of the organization (read-only). billing_state: free (no paid subscription), active, canceling (ends at period end, then free limits), grace (payment overdue, workspaces unaffected), restricted (grace ended: new opens/resumes answer 402 entitlement_required).
Authentication: API key
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
organization_id |
path | string (uuid) | yes | UUIDv7, lowercase canonical form. |
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
Client versions
The latest and the oldest supported version of every Shardflux client package.
GET /v1/client-versions
Latest and minimum supported version of every Shardflux client.
Unauthenticated, Cache-Control: public, max-age=3600. Clients compare their own version: below minimum_supported they are unsupported, below latest outdated. They check at most once a day (the CLI) or once per process (the SDKs), in the background, and never fail because of it.
Authentication: None (public)
Responses: 200 · 4XX ErrorBody · 5XX ErrorBody
Feedback
Send product feedback straight to the Shardflux team: what failed, confused you or is missing.
POST /v1/feedback
Send product feedback to the Shardflux team.
Delivered by email to the Shardflux team. Any valid project API key may send (no tool permission needed); the key's organization and project are recorded, so organization_id is rejected (422). A CLI session (sfu_) may send too, with an optional organization_id of the user's. Coding agents: send feedback whenever something fails, is confusing or missing, and include context (agent, client, workspace, request_id and error_code of the error response, command). Rate limited per sender (10 per 10 minutes, 50 per day) and per organization (200 per day): 429 rate_limited with a retry-after header and details.retry_after_seconds. The same message from the same sender within 24 hours is a duplicate: 200 with the original id and duplicate: true (it still counts against the rate limits). The body is limited to 32 KiB (413).
Authentication: API key
Request body (application/json, required)
| Field | Type | Required | Description |
|---|---|---|---|
message |
string | yes | The feedback, 1-8000 characters after trimming surrounding whitespace (must contain a non-whitespace character). Control characters other than newline and tab are stripped; API keys, bearer tokens, JWTs, AWS access key ids and private key blocks are redacted before it is stored or emailed. |
category |
"bug" or "confusing" or "missing" or "idea" or "praise" or "other" | no | Default other. bug: something failed or behaved wrongly; confusing: an error, doc, name or output was unclear or misleading; missing: a capability, option or template you needed does not exist; idea: a suggestion or improvement; praise: something worked well; other: anything else. |
organization_id |
string (uuid) | no | Sessions (browser, or a CLI session on /v1): the organization the user is looking at, one they belong to (404 otherwise). Refused (422) with a project API key, whose organization is used. |
context |
object | no | Optional, every field optional. Control characters are stripped and anything shaped like a secret is redacted before storing. |
Responses: 200 · 201 · 4XX ErrorBody · 5XX ErrorBody