Get started
Quick start
Create a workspace and complete your first Run.
On this page
Before you start
Open the Platform console, bind a card without payment to claim your $5 new-account Promotion Credits (valid until 30 days after registration) or add prepaid credit, and create a personal API key or a key for your Team Project. Copy the key when it is shown; it cannot be viewed again. These commands use curl.
export SANDPI_API_TOKEN="your-platform-token"Commands below come from the API contract. Replace placeholders such as YOUR_SUPPORTED_MODEL_ID and YOUR_SESSION_ID with your own values. Path variables use the returned resource IDs.
Choose a model and harness
Choose a native Harness and prepare your model API base URL and API key. Your gateway must support that Harness's native API. See model connections for the protocol mapping. Model charges go to your provider; Sandpi bills actual Sandbox runtime by the second, at a rate equivalent to $0.10/hour for a fixed 2 vCPU / 4 GiB Sandbox.
Create a persistent Environment
Create a managed Environment
/v1/environmentsOptional networkPolicy is stored before Sandbox provisioning and repository initialization. If omitted, outbound access defaults to allow-all with no domain exceptions. Allow required repository and dependency hosts when initializing a restricted Environment; model connection URLs never automatically grant outbound access.
Authentication: Platform API bearer token
Request example
curl -X POST \
"https://api.sandpi.ai/v1/environments" \
-H "Authorization: Bearer $SANDPI_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "PR review workspace",
"labels": {
"repo": "sandpi",
"purpose": "review"
}
}'Request body · required application/json
| Field | Type and constraints | Required | Description |
|---|---|---|---|
name | string · min length 1; max length 100Validation pattern.*\S.* | Yes | Human-readable name for this resource. |
repository | object | No | Public HTTPS Git repository used to initialize the workspace. |
repository.url | string · format: uri; max length 2048Validation pattern^https:\/\/.* | Yeswithin repository | Public HTTPS Git URL without credentials, a port, query, fragment, or private host. |
repository.ref | stringValidation pattern^(?!.*\.\.)[A-Za-z0-9][A-Za-z0-9._/-]{0,199}$ | Nowithin repository | Optional repository branch or tag to clone during initialization. |
projectId | string · min length 1 | No | Optional Project scope; must match the API token Project. |
labels | object · max 32 keys | No | User-defined labels for exact-match filtering. Up to 32 entries. Keys are 1–64 ASCII letters, digits, dots, underscores, slashes or hyphens, beginning with a letter or digit; constructor and prototype are reserved. Values are case-sensitive strings of up to 256 characters, including empty strings. |
labels.{key} | string · max length 256 | Noadditional keys | — |
networkPolicy | object · extra fields rejected | No | Environment-wide outbound policy, enforced by Sandbox0. Domain exceptions allow destinations in block-all mode and deny destinations in allow-all mode. Enabled egress credentials also allow their destinations in block-all mode. This policy does not restrict HTTP methods, paths, or local commands. |
networkPolicy.mode | string · values: "allow-all" | "block-all" | Yeswithin networkPolicy | Default for outbound traffic not matched by a domain exception or enabled credential destination. |
networkPolicy.domainExceptions | array · max 500 items | Yeswithin networkPolicy | Domains allowed in block-all mode or denied in allow-all mode. Exact names do not automatically include subdomains. |
networkPolicy.domainExceptions[] | string · min length 1; max length 255 | Yeseach array item | Exact domain or wildcard suffix such as *.example.com. URLs, paths, ports and IP literals are not supported. Names are normalized to lowercase and deduplicated. |
Example
{
"name": "PR review workspace",
"labels": {
"repo": "sandpi",
"purpose": "review"
}
}Responses
201 Created.Response body application/json
| Field | Type and constraints | Required | Description |
|---|---|---|---|
data | object | Yes | Run completion settles the file diff and RootFS checkpoint, then immediately requests a memory pause when no Run, rollback, or checkpoint remains. File and other runtime access uses a one-minute inactivity fallback. Queued Runs continue without an intervening pause. The interactive TUI retains its separate fifteen-minute idle policy. On access after a confirmed memory restoration failure, Sandpi automatically requests a durable cold restart from committed files for a qualified stopped source. Processes restart and memory or uncommitted writes may be lost. Pending or uncertain restores retain their original operation. |
data.id | string | Yeswithin data | Resource identifier used to read or manage this resource. |
data.name | string | Yeswithin data | Human-readable name for this resource. |
data.status | string · values: "ready" | "updating" | "error" | Yeswithin data | Current resource state; inspect the allowed values and the operation’s lifecycle notes. |
data.platform | object · extra fields rejected | Yeswithin data | Platform checkpoint, workspace revision and initialization metadata. |
data.platform.initialCheckpointStatus | string · values: "pending" | "ready" | "failed" | Yeswithin data | Initialization checkpoint state. Wait for ready before submitting work. |
data.platform.initialCheckpointSnapshotId | string | Nowithin data | Initial immutable RootFS checkpoint identifier, present after capture succeeds. |
data.platform.initialCheckpointError | string | Nowithin data | Diagnostic text for initialization checkpoint failure. |
data.platform.workspaceRevision | integer · ≥ -9007199254740991; ≤ 9007199254740991 | Yeswithin data | Observed workspace revision used to fence conflicting changes and fork operations. |
data.platform.forkedFrom | object · extra fields rejected | Nowithin data | Source Environment and selected fork boundary; present for forked workspaces. |
data.platform.forkedFrom.environmentId | string | Yeswithin data | Environment identifier. It must belong to the caller’s account or Project. |
data.platform.forkedFrom.mode | string · values: "current" | "checkpoint" | Yeswithin data | Whether the fork copied current workspace files or a selected immutable checkpoint. |
data.platform.forkedFrom.snapshotId | string | Nowithin data | Sandbox0 checkpoint identifier. Use a returned initial or Run checkpoint when pinning a fork. |
data.platform.forkedFrom.revision | integer · ≥ -9007199254740991; ≤ 9007199254740991 | Yeswithin data | Source Environment Run revision observed when the fork was created. |
data.platform.repository | object · extra fields rejected | Nowithin data | Public HTTPS Git repository used to initialize the workspace. |
data.platform.repository.url | string · format: uri | Yeswithin data | Public HTTPS Git URL used to initialize the workspace. |
data.platform.repository.ref | string | Nowithin data | Optional repository branch or tag to clone during initialization. |
data.platform.repository.initialized | boolean | Yeswithin data | Whether repository initialization has finished. |
data.labels | object · max 32 keys | Yeswithin data | User-defined labels for exact-match filtering. Up to 32 entries. Keys are 1–64 ASCII letters, digits, dots, underscores, slashes or hyphens, beginning with a letter or digit; constructor and prototype are reserved. Values are case-sensitive strings of up to 256 characters, including empty strings. |
data.labels.{key} | string · max length 256 | Noadditional keys | — |
data.labelsRevision | integer · ≥ 0; ≤ 2147483647 | Yeswithin data | Independent labels revision. Supply the last read revision when replacing labels; a stale revision returns 409. |
data.networkPolicy | object · extra fields rejected | Yeswithin data | Environment-wide outbound policy, enforced by Sandbox0. Domain exceptions allow destinations in block-all mode and deny destinations in allow-all mode. Enabled egress credentials also allow their destinations in block-all mode. This policy does not restrict HTTP methods, paths, or local commands. |
data.networkPolicy.mode | string · values: "allow-all" | "block-all" | Yeswithin data | Default for outbound traffic not matched by a domain exception or enabled credential destination. |
data.networkPolicy.domainExceptions | array · max 500 items | Yeswithin data | Domains allowed in block-all mode or denied in allow-all mode. Exact names do not automatically include subdomains. |
data.networkPolicy.domainExceptions[] | string · min length 1; max length 255 | Yeseach array item | Exact domain or wildcard suffix such as *.example.com. URLs, paths, ports and IP literals are not supported. Names are normalized to lowercase and deduplicated. |
data.{key} | any | Noadditional keys | — |
Example
{
"data": {
"id": "",
"name": "",
"status": "ready",
"platform": {
"initialCheckpointStatus": "pending",
"workspaceRevision": -9007199254740991
},
"labels": {},
"labelsRevision": 0,
"networkPolicy": {
"mode": "allow-all",
"domainExceptions": [
"example"
]
}
}
}Error responses
400 Invalid request.application/json
401 Missing or invalid Platform API token.application/json
402 Insufficient Platform credit.application/json
403 Token cannot create in the requested Project.application/json
415 Unsupported request media type.application/json
503 Platform execution is unavailable.application/json
default Sandpi error.Error body application/json
Standard error envelope containing a stable code, diagnostic message and request identity.
| Field | Type and constraints | Required | Description |
|---|---|---|---|
error | object · extra fields rejected | Yes | Structured failure information. Inspect the stable code and retain the request ID for investigation. |
error.code | string | Yeswithin error | Stable error code used to distinguish failures programmatically. |
error.message | string | Yeswithin error | Human-readable diagnostic text. |
error.requestId | string | Yeswithin error | Request identifier for correlating diagnostics and support investigations. |
error.details | any | Nowithin error | Optional structured error context. Its shape is specific to the service failure. |
error.loginUrl | string | Nowithin error | Authentication URL supplied when a browser login is required. |
error.registrationOpen | boolean | Nowithin error | Whether new account registration is enabled for this deployment. |
Example
{
"error": {
"code": "platform_balance_insufficient",
"message": "Top up Platform credits before submitting a Run.",
"requestId": "req_example"
}
}The JSON result is wrapped in data. Save data.id as ENVIRONMENT_ID. Poll the Environment until its status and platform.initialCheckpointStatus are both ready. A successful creation response can precede initialization completion.
export ENVIRONMENT_ID="returned-environment-id"Get a managed Environment
/v1/environments/{environmentId}Authentication: Platform API bearer token
Parameters and headers
| Field | Type and constraints | Required | Description |
|---|---|---|---|
path: environmentId | string · min length 1 | Yes | Environment identifier. It must belong to the caller’s account or Project. |
Request example
curl \
"https://api.sandpi.ai/v1/environments/$ENVIRONMENT_ID" \
-H "Authorization: Bearer $SANDPI_API_TOKEN"No request body.
Responses
200 Successful response.Response body application/json
| Field | Type and constraints | Required | Description |
|---|---|---|---|
data | object | Yes | Run completion settles the file diff and RootFS checkpoint, then immediately requests a memory pause when no Run, rollback, or checkpoint remains. File and other runtime access uses a one-minute inactivity fallback. Queued Runs continue without an intervening pause. The interactive TUI retains its separate fifteen-minute idle policy. On access after a confirmed memory restoration failure, Sandpi automatically requests a durable cold restart from committed files for a qualified stopped source. Processes restart and memory or uncommitted writes may be lost. Pending or uncertain restores retain their original operation. |
data.id | string | Yeswithin data | Resource identifier used to read or manage this resource. |
data.name | string | Yeswithin data | Human-readable name for this resource. |
data.status | string · values: "ready" | "updating" | "error" | Yeswithin data | Current resource state; inspect the allowed values and the operation’s lifecycle notes. |
data.platform | object · extra fields rejected | Yeswithin data | Platform checkpoint, workspace revision and initialization metadata. |
data.platform.initialCheckpointStatus | string · values: "pending" | "ready" | "failed" | Yeswithin data | Initialization checkpoint state. Wait for ready before submitting work. |
data.platform.initialCheckpointSnapshotId | string | Nowithin data | Initial immutable RootFS checkpoint identifier, present after capture succeeds. |
data.platform.initialCheckpointError | string | Nowithin data | Diagnostic text for initialization checkpoint failure. |
data.platform.workspaceRevision | integer · ≥ -9007199254740991; ≤ 9007199254740991 | Yeswithin data | Observed workspace revision used to fence conflicting changes and fork operations. |
data.platform.forkedFrom | object · extra fields rejected | Nowithin data | Source Environment and selected fork boundary; present for forked workspaces. |
data.platform.forkedFrom.environmentId | string | Yeswithin data | Environment identifier. It must belong to the caller’s account or Project. |
data.platform.forkedFrom.mode | string · values: "current" | "checkpoint" | Yeswithin data | Whether the fork copied current workspace files or a selected immutable checkpoint. |
data.platform.forkedFrom.snapshotId | string | Nowithin data | Sandbox0 checkpoint identifier. Use a returned initial or Run checkpoint when pinning a fork. |
data.platform.forkedFrom.revision | integer · ≥ -9007199254740991; ≤ 9007199254740991 | Yeswithin data | Source Environment Run revision observed when the fork was created. |
data.platform.repository | object · extra fields rejected | Nowithin data | Public HTTPS Git repository used to initialize the workspace. |
data.platform.repository.url | string · format: uri | Yeswithin data | Public HTTPS Git URL used to initialize the workspace. |
data.platform.repository.ref | string | Nowithin data | Optional repository branch or tag to clone during initialization. |
data.platform.repository.initialized | boolean | Yeswithin data | Whether repository initialization has finished. |
data.labels | object · max 32 keys | Yeswithin data | User-defined labels for exact-match filtering. Up to 32 entries. Keys are 1–64 ASCII letters, digits, dots, underscores, slashes or hyphens, beginning with a letter or digit; constructor and prototype are reserved. Values are case-sensitive strings of up to 256 characters, including empty strings. |
data.labels.{key} | string · max length 256 | Noadditional keys | — |
data.labelsRevision | integer · ≥ 0; ≤ 2147483647 | Yeswithin data | Independent labels revision. Supply the last read revision when replacing labels; a stale revision returns 409. |
data.networkPolicy | object · extra fields rejected | Yeswithin data | Environment-wide outbound policy, enforced by Sandbox0. Domain exceptions allow destinations in block-all mode and deny destinations in allow-all mode. Enabled egress credentials also allow their destinations in block-all mode. This policy does not restrict HTTP methods, paths, or local commands. |
data.networkPolicy.mode | string · values: "allow-all" | "block-all" | Yeswithin data | Default for outbound traffic not matched by a domain exception or enabled credential destination. |
data.networkPolicy.domainExceptions | array · max 500 items | Yeswithin data | Domains allowed in block-all mode or denied in allow-all mode. Exact names do not automatically include subdomains. |
data.networkPolicy.domainExceptions[] | string · min length 1; max length 255 | Yeseach array item | Exact domain or wildcard suffix such as *.example.com. URLs, paths, ports and IP literals are not supported. Names are normalized to lowercase and deduplicated. |
data.{key} | any | Noadditional keys | — |
Example
{
"data": {
"id": "",
"name": "",
"status": "ready",
"platform": {
"initialCheckpointStatus": "pending",
"workspaceRevision": -9007199254740991
},
"labels": {},
"labelsRevision": 0,
"networkPolicy": {
"mode": "allow-all",
"domainExceptions": [
"example"
]
}
}
}Error responses
400 Invalid request.application/json
401 Missing or invalid Platform API token.application/json
404 Resource not found or outside token scope.application/json
default Sandpi error.Error body application/json
Standard error envelope containing a stable code, diagnostic message and request identity.
| Field | Type and constraints | Required | Description |
|---|---|---|---|
error | object · extra fields rejected | Yes | Structured failure information. Inspect the stable code and retain the request ID for investigation. |
error.code | string | Yeswithin error | Stable error code used to distinguish failures programmatically. |
error.message | string | Yeswithin error | Human-readable diagnostic text. |
error.requestId | string | Yeswithin error | Request identifier for correlating diagnostics and support investigations. |
error.details | any | Nowithin error | Optional structured error context. Its shape is specific to the service failure. |
error.loginUrl | string | Nowithin error | Authentication URL supplied when a browser login is required. |
error.registrationOpen | boolean | Nowithin error | Whether new account registration is enabled for this deployment. |
Example
{
"error": {
"code": "platform_balance_insufficient",
"message": "Top up Platform credits before submitting a Run.",
"requestId": "req_example"
}
}Create a harness Session
The Session retains native conversation history across Runs. Supply your native connection when creating the Session. Pi additionally requires its API type.
Create a harness Session
/v1/environments/{environmentId}/sessionsCreate a Session using a model vendor’s native Harness and your own API or gateway. Pi requires connection.api. No Sandpi Token purchase or model-price catalog is needed.
Authentication: Platform API bearer token
Parameters and headers
| Field | Type and constraints | Required | Description |
|---|---|---|---|
path: environmentId | string · min length 1 | Yes | Environment identifier. It must belong to the caller’s account or Project. |
Request example
curl -X POST \
"https://api.sandpi.ai/v1/environments/$ENVIRONMENT_ID/sessions" \
-H "Authorization: Bearer $SANDPI_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"harness": "codex",
"connection": {
"baseUrl": "https://gateway.example.com/v1",
"apiKey": "YOUR_GATEWAY_KEY"
}
}'Request body · required application/json
| Field | Type and constraints | Required | Description |
|---|---|---|---|
harness | string · values: "codex" | "claude-code" | "pi" | "kimi-code" | "zcode" | Yes | Native coding-agent program used by the Session. |
connection | object · extra fields rejected | Yes | — |
connection.baseUrl | string · format: uri; max length 2048 | Yeswithin connection | Native model API base URL, including its required path prefix. Harness connects directly; Sandpi does not translate model traffic. |
connection.apiKey | string · min length 1; max length 8192; write only | Yeswithin connection | Your provider or gateway credential. Encrypted at rest, never returned by the API. |
connection.headers | object · max 32 keys; write only | Nowithin connection | Additional native HTTP headers. Stored with the encrypted credential. |
connection.headers.{key} | string · max length 8192 | Noadditional keys | — |
connection.api | string · values: "openai-responses" | "openai-completions" | "anthropic-messages" | Nowithin connection | Pi native API adapter. Required for Pi; other Harnesses use their native API. |
connection.contextWindow | integer · ≥ 1024; ≤ 10000000 | Nowithin connection | Pi custom model context capacity; defaults to 128000. |
connection.maxOutputTokens | integer · ≥ 1; ≤ 1000000 | Nowithin connection | Pi custom model output capacity; defaults to 16384. |
sandboxBudgetUsd | number · nullable; ≥ 0; ≤ 100000 | No | Optional lifetime Sandbox execution budget in USD. Converted to cumulative Harness execution time; does not add a Sandbox charge. Omitted or null is unlimited. |
Example
{
"harness": "codex",
"connection": {
"baseUrl": "https://gateway.example.com/v1",
"apiKey": "YOUR_GATEWAY_KEY"
}
}Responses
201 Created.Response body application/json
| Field | Type and constraints | Required | Description |
|---|---|---|---|
data | object · extra fields rejected | Yes | Persistent native Harness conversation. Configure your own provider or gateway; Sandpi charges only Sandbox runtime. |
data.id | string | Yeswithin data | Resource identifier used to read or manage this resource. |
data.environmentId | string | Yeswithin data | Environment identifier. It must belong to the caller’s account or Project. |
data.harness | string · values: "codex" | "claude-code" | "pi" | "kimi-code" | "zcode" | Yeswithin data | Native coding-agent program used by the Session. |
data.provider | string · values: "openai" | "anthropic" | "kimi" | "zai" | "deepseek" | Yeswithin data | Native model family associated with this Harness; not a Sandpi Token supplier. |
data.nativeSessionId | string | Nowithin data | Native harness conversation identifier retained for Session resume. |
data.nativeHistoryState | string · values: "new" | "available" | "unavailable" | Yeswithin data | State of the Session’s native conversation history. |
data.createdAt | string · format: date-timeValidation pattern^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$ | Yeswithin data | Creation time as a UTC date-time string. |
data.connection | object · extra fields rejected | Nowithin data | — |
data.connection.baseUrl | string · format: uri; max length 2048 | Yeswithin data | Native model API base URL, including its required path prefix. Harness connects directly; Sandpi does not translate model traffic. |
data.connection.api | string · values: "openai-responses" | "openai-completions" | "anthropic-messages" | Nowithin data | Pi native API adapter. Required for Pi; other Harnesses use their native API. |
data.connection.revision | integer · ≥ 1 | Yeswithin data | Immutable connection revision captured by new Runs. Rotation does not change an existing Run. |
Example
{
"data": {
"id": "",
"environmentId": "",
"harness": "codex",
"provider": "openai",
"nativeHistoryState": "new",
"createdAt": "2026-10-12T01:00:00Z"
}
}Error responses
400 Invalid request.application/json
401 Missing or invalid Platform API token.application/json
404 Resource not found or outside token scope.application/json
415 Unsupported request media type.application/json
503 The selected harness or model route is temporarily unavailable.application/json
default Sandpi error.Error body application/json
Standard error envelope containing a stable code, diagnostic message and request identity.
| Field | Type and constraints | Required | Description |
|---|---|---|---|
error | object · extra fields rejected | Yes | Structured failure information. Inspect the stable code and retain the request ID for investigation. |
error.code | string | Yeswithin error | Stable error code used to distinguish failures programmatically. |
error.message | string | Yeswithin error | Human-readable diagnostic text. |
error.requestId | string | Yeswithin error | Request identifier for correlating diagnostics and support investigations. |
error.details | any | Nowithin error | Optional structured error context. Its shape is specific to the service failure. |
error.loginUrl | string | Nowithin error | Authentication URL supplied when a browser login is required. |
error.registrationOpen | boolean | Nowithin error | Whether new account registration is enabled for this deployment. |
Example
{
"error": {
"code": "platform_balance_insufficient",
"message": "Top up Platform credits before submitting a Run.",
"requestId": "req_example"
}
}Save data.id as SESSION_ID. A Platform Session is a headless conversation; it is separate from an interactive TUI terminal.
export SESSION_ID="returned-session-id"
export REQUEST_KEY="$(uuidgen)"Submit your first Run
Queue an idempotent headless Run
/v1/environments/{environmentId}/sessions/{sessionId}/runsRequires Idempotency-Key. Runs execute serially within an Environment.
Authentication: Platform API bearer token
Parameters and headers
| Field | Type and constraints | Required | Description |
|---|---|---|---|
path: environmentId | string · min length 1 | Yes | Environment identifier. It must belong to the caller’s account or Project. |
path: sessionId | string · min length 1 | Yes | Existing Session identifier within the named Environment. |
header: idempotency-key | string · min length 1; max length 128 | Yes | Stable key for one logical operation. Retry with the same key and exact request body; a new key can admit new work. |
Request example
curl -X POST \
"https://api.sandpi.ai/v1/environments/$ENVIRONMENT_ID/sessions/$SESSION_ID/runs" \
-H "Authorization: Bearer $SANDPI_API_TOKEN" \
-H "idempotency-key: $REQUEST_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "YOUR_SUPPORTED_MODEL_ID",
"input": "Fix the login error"
}'Request body · required application/json
| Field | Type and constraints | Required | Description |
|---|---|---|---|
model | string · min length 1; max length 200Validation pattern.*\S.* | Yes | Exact native model ID served by your provider or gateway. |
input | string · min length 1 | Yes | Instructions supplied to the agent for this task. |
files | array · max 20 items | No | Files associated with this resource; each item is described below. |
files[] | object | Yeseach array item | — |
files[].path | string · min length 1; max length 2048 | Yeseach array item | Canonical absolute path inside /workspace. Protected state, Git metadata and symlinks are not accepted file targets. |
files[].sha256 | stringValidation pattern^[a-f0-9]{64}$ | Noeach array item | Lowercase hexadecimal SHA-256 digest of the file bytes. |
Example
{
"model": "YOUR_SUPPORTED_MODEL_ID",
"input": "Fix the login error"
}Responses
202 Accepted.Response body application/json
| Field | Type and constraints | Required | Description |
|---|---|---|---|
data | object · extra fields rejected | Yes | Native task execution, output, file diff and checkpoint state. These states settle independently. |
data.id | string | Yeswithin data | Resource identifier used to read or manage this resource. |
data.environmentId | string | Yeswithin data | Environment identifier. It must belong to the caller’s account or Project. |
data.sessionId | string | Yeswithin data | Existing Session identifier within the named Environment. |
data.environmentSequence | integer · ≥ -9007199254740991; ≤ 9007199254740991 | Yeswithin data | Environment-wide Run ordering across all Sessions; rollback uses this ordering. |
data.model | string | Yeswithin data | Exact native model ID served by your provider or gateway. |
data.input | string | Yeswithin data | Instructions supplied to the agent for this task. |
data.files | array | Yeswithin data | Files associated with this resource; each item is described below. |
data.files[] | object · extra fields rejected | Yeseach array item | Reference to an existing regular workspace file checked before execution. |
data.files[].path | string | Yeseach array item | Canonical absolute path inside /workspace. Protected state, Git metadata and symlinks are not accepted file targets. |
data.files[].sha256 | stringValidation pattern^[a-f0-9]{64}$ | Noeach array item | Lowercase hexadecimal SHA-256 digest of the file bytes. |
data.status | string · values: "queued" | "running" | "completed" | "failed" | "stopped" | "reverting" | "reverted" | Yeswithin data | Current resource state; inspect the allowed values and the operation’s lifecycle notes. |
data.stopRequested | boolean | Yeswithin data | Whether a client has requested that the native task stop. |
data.output | string | Nowithin data | Final agent output when available. Content retention may erase this field. |
data.error | object · extra fields rejected | Nowithin data | Execution failure or transient runtime availability diagnostic. While status is running, platform_runtime_unavailable means observation or preparation is blocked; the original task may still be executing. Reuse this Run instead of submitting it again. The diagnostic clears after successful runtime access. Optional timestamped resource samples may be appended asynchronously; these measurements do not establish the timeout cause or OOM. |
data.error.code | string | Nowithin data | Stable code identifying an execution failure or transient runtime availability diagnostic. |
data.error.message | string | Nowithin data | Human-readable failure or availability diagnostic; it does not by itself establish that the original task stopped. |
data.fileDiff | array | Yeswithin data | Manifest of added, modified and deleted regular workspace files. It does not contain file bytes. |
data.fileDiff[] | object · extra fields rejected | Yeseach array item | Regular workspace file change with before and after content digests. |
data.fileDiff[].path | string | Yeseach array item | Canonical absolute path inside /workspace. Protected state, Git metadata and symlinks are not accepted file targets. |
data.fileDiff[].kind | string · values: "added" | "modified" | "deleted" | Yeseach array item | Whether the Run added, modified or deleted the regular file. |
data.fileDiff[].beforeSha256 | stringValidation pattern^[a-f0-9]{64}$ | Noeach array item | Content digest before the Run; absent for a newly added file. |
data.fileDiff[].afterSha256 | stringValidation pattern^[a-f0-9]{64}$ | Noeach array item | Content digest after the Run; absent for a deleted file. |
data.fileDiffStatus | string · values: "pending" | "ready" | "failed" | Yeswithin data | File-diff computation state, separate from execution and checkpoint state. |
data.fileDiffError | string | Nowithin data | Diagnostic text when file-diff computation fails. |
data.checkpoint | object · extra fields rejected | Yeswithin data | RootFS checkpoint state and identifier, tracked separately from native execution. |
data.checkpoint.status | string · values: "pending" | "ready" | "failed" | "skipped" | Yeswithin data | Current resource state; inspect the allowed values and the operation’s lifecycle notes. |
data.checkpoint.snapshotId | string | Nowithin data | Sandbox0 checkpoint identifier. Use a returned initial or Run checkpoint when pinning a fork. |
data.checkpoint.error | string | Nowithin data | Structured failure information. Inspect the stable code and retain the request ID for investigation. |
data.createdAt | string · format: date-timeValidation pattern^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$ | Yeswithin data | Creation time as a UTC date-time string. |
data.startedAt | string · format: date-timeValidation pattern^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$ | Nowithin data | Execution start time as a UTC date-time string. |
data.finishedAt | string · format: date-timeValidation pattern^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$ | Nowithin data | Terminal execution time as a UTC date-time string. |
data.steeringEnabled | boolean | Yeswithin data | Whether this Run supports native steering during execution. |
data.contentDeletedAt | string · format: date-time | Nowithin data | Platform content was erased; native history and checkpoints remain until Environment deletion. |
Example
{
"data": {
"id": "",
"environmentId": "",
"sessionId": "",
"environmentSequence": -9007199254740991,
"model": "",
"input": "",
"files": [
{
"path": ""
}
],
"status": "queued",
"stopRequested": false,
"fileDiff": [
{
"path": "",
"kind": "added"
}
],
"fileDiffStatus": "pending",
"checkpoint": {
"status": "pending"
},
"createdAt": "2026-10-12T01:00:00Z",
"steeringEnabled": false
}
}Error responses
400 Invalid request.application/json
401 Missing or invalid Platform API token.application/json
402 Insufficient Platform credit.application/json
404 Resource not found or outside token scope.application/json
409 Resource state conflict.application/json
415 Unsupported request media type.application/json
503 Platform execution is unavailable.application/json
default Sandpi error.Error body application/json
Standard error envelope containing a stable code, diagnostic message and request identity.
| Field | Type and constraints | Required | Description |
|---|---|---|---|
error | object · extra fields rejected | Yes | Structured failure information. Inspect the stable code and retain the request ID for investigation. |
error.code | string | Yeswithin error | Stable error code used to distinguish failures programmatically. |
error.message | string | Yeswithin error | Human-readable diagnostic text. |
error.requestId | string | Yeswithin error | Request identifier for correlating diagnostics and support investigations. |
error.details | any | Nowithin error | Optional structured error context. Its shape is specific to the service failure. |
error.loginUrl | string | Nowithin error | Authentication URL supplied when a browser login is required. |
error.registrationOpen | boolean | Nowithin error | Whether new account registration is enabled for this deployment. |
Example
{
"error": {
"code": "platform_balance_insufficient",
"message": "Top up Platform credits before submitting a Run.",
"requestId": "req_example"
}
}Save the returned data.id as RUN_ID. Keep the same request key and body if the connection fails during submission. A new key means new work. Runs in one Environment execute serially, including Runs from different Sessions.
export RUN_ID="returned-run-id"Follow execution and collect the result
Replay Run events over SSE
/v1/environments/{environmentId}/sessions/{sessionId}/runs/{runId}/eventsAuthentication: Platform API bearer token
Parameters and headers
| Field | Type and constraints | Required | Description |
|---|---|---|---|
query: after | integer · ≥ 0; ≤ 9007199254740991 | No | Last processed event ID or pagination cursor. Return only subsequent records; retain it when reconnecting or fetching another page. |
path: environmentId | string · min length 1 | Yes | Environment identifier. It must belong to the caller’s account or Project. |
path: sessionId | string · min length 1 | Yes | Existing Session identifier within the named Environment. |
path: runId | string · min length 1 | Yes | Run identifier within the named Environment and, where applicable, Session. |
header: last-event-id | stringValidation pattern^[0-9]+$ | No | Last received event ID for replay. |
Request example
curl -N \
"https://api.sandpi.ai/v1/environments/$ENVIRONMENT_ID/sessions/$SESSION_ID/runs/$RUN_ID/events" \
-H "Authorization: Bearer $SANDPI_API_TOKEN" \
-H "last-event-id: 42"No request body.
Responses
200 Successful response.Response body text/event-stream
| Field | Type and constraints | Required | Description |
|---|---|---|---|
body | string | Yes | — |
Example
id: 42
event: run.output
data: {"id":42,"type":"run.output"}
Stream event names
run.queuedrun.runningrun.nativerun.outputrun.completedrun.failedrun.stoppedrun.stop_requestedrun.revertingrun.revertedrun.file_diff_readyrun.file_diff_failedrun.checkpoint_readyrun.message.queuedrun.message.acceptedrun.message.rejectedrun.message.unconfirmedrun.native_finishedrun.runtime_unavailablerun.runtime_recoveredrun.resource_observationError responses
400 Invalid request.application/json
401 Missing or invalid Platform API token.application/json
404 Resource not found or outside token scope.application/json
default Sandpi error.Error body application/json
Standard error envelope containing a stable code, diagnostic message and request identity.
| Field | Type and constraints | Required | Description |
|---|---|---|---|
error | object · extra fields rejected | Yes | Structured failure information. Inspect the stable code and retain the request ID for investigation. |
error.code | string | Yeswithin error | Stable error code used to distinguish failures programmatically. |
error.message | string | Yeswithin error | Human-readable diagnostic text. |
error.requestId | string | Yeswithin error | Request identifier for correlating diagnostics and support investigations. |
error.details | any | Nowithin error | Optional structured error context. Its shape is specific to the service failure. |
error.loginUrl | string | Nowithin error | Authentication URL supplied when a browser login is required. |
error.registrationOpen | boolean | Nowithin error | Whether new account registration is enabled for this deployment. |
Example
{
"error": {
"code": "platform_balance_insufficient",
"message": "Top up Platform credits before submitting a Run.",
"requestId": "req_example"
}
}The SSE connection can be closed without stopping the Run. Reconnect using the last event ID rather than resubmitting the task.
Get Run state and Sandbox usage
/v1/environments/{environmentId}/sessions/{sessionId}/runs/{runId}Authentication: Platform API bearer token
Parameters and headers
| Field | Type and constraints | Required | Description |
|---|---|---|---|
path: environmentId | string · min length 1 | Yes | Environment identifier. It must belong to the caller’s account or Project. |
path: sessionId | string · min length 1 | Yes | Existing Session identifier within the named Environment. |
path: runId | string · min length 1 | Yes | Run identifier within the named Environment and, where applicable, Session. |
Request example
curl \
"https://api.sandpi.ai/v1/environments/$ENVIRONMENT_ID/sessions/$SESSION_ID/runs/$RUN_ID" \
-H "Authorization: Bearer $SANDPI_API_TOKEN"No request body.
Responses
200 Successful response.Response body application/json
| Field | Type and constraints | Required | Description |
|---|---|---|---|
data | object · extra fields rejected | Yes | Run details with posted Sandbox runtime and retained historical managed-model settlement; BYOK model costs are billed by your provider. |
data.id | string | Yeswithin data | Resource identifier used to read or manage this resource. |
data.environmentId | string | Yeswithin data | Environment identifier. It must belong to the caller’s account or Project. |
data.sessionId | string | Yeswithin data | Existing Session identifier within the named Environment. |
data.environmentSequence | integer · ≥ -9007199254740991; ≤ 9007199254740991 | Yeswithin data | Environment-wide Run ordering across all Sessions; rollback uses this ordering. |
data.model | string | Yeswithin data | Exact native model ID served by your provider or gateway. |
data.input | string | Yeswithin data | Instructions supplied to the agent for this task. |
data.files | array | Yeswithin data | Files associated with this resource; each item is described below. |
data.files[] | object · extra fields rejected | Yeseach array item | Reference to an existing regular workspace file checked before execution. |
data.files[].path | string | Yeseach array item | Canonical absolute path inside /workspace. Protected state, Git metadata and symlinks are not accepted file targets. |
data.files[].sha256 | stringValidation pattern^[a-f0-9]{64}$ | Noeach array item | Lowercase hexadecimal SHA-256 digest of the file bytes. |
data.status | string · values: "queued" | "running" | "completed" | "failed" | "stopped" | "reverting" | "reverted" | Yeswithin data | Current resource state; inspect the allowed values and the operation’s lifecycle notes. |
data.stopRequested | boolean | Yeswithin data | Whether a client has requested that the native task stop. |
data.output | string | Nowithin data | Final agent output when available. Content retention may erase this field. |
data.fileDiff | array | Yeswithin data | Manifest of added, modified and deleted regular workspace files. It does not contain file bytes. |
data.fileDiff[] | object · extra fields rejected | Yeseach array item | Regular workspace file change with before and after content digests. |
data.fileDiff[].path | string | Yeseach array item | Canonical absolute path inside /workspace. Protected state, Git metadata and symlinks are not accepted file targets. |
data.fileDiff[].kind | string · values: "added" | "modified" | "deleted" | Yeseach array item | Whether the Run added, modified or deleted the regular file. |
data.fileDiff[].beforeSha256 | stringValidation pattern^[a-f0-9]{64}$ | Noeach array item | Content digest before the Run; absent for a newly added file. |
data.fileDiff[].afterSha256 | stringValidation pattern^[a-f0-9]{64}$ | Noeach array item | Content digest after the Run; absent for a deleted file. |
data.fileDiffStatus | string · values: "pending" | "ready" | "failed" | Yeswithin data | File-diff computation state, separate from execution and checkpoint state. |
data.fileDiffError | string | Nowithin data | Diagnostic text when file-diff computation fails. |
data.checkpoint | object · extra fields rejected | Yeswithin data | RootFS checkpoint state and identifier, tracked separately from native execution. |
data.checkpoint.status | string · values: "pending" | "ready" | "failed" | "skipped" | Yeswithin data | Current resource state; inspect the allowed values and the operation’s lifecycle notes. |
data.checkpoint.snapshotId | string | Nowithin data | Sandbox0 checkpoint identifier. Use a returned initial or Run checkpoint when pinning a fork. |
data.checkpoint.error | string | Nowithin data | Structured failure information. Inspect the stable code and retain the request ID for investigation. |
data.createdAt | string · format: date-timeValidation pattern^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$ | Yeswithin data | Creation time as a UTC date-time string. |
data.startedAt | string · format: date-timeValidation pattern^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$ | Nowithin data | Execution start time as a UTC date-time string. |
data.finishedAt | string · format: date-timeValidation pattern^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$ | Nowithin data | Terminal execution time as a UTC date-time string. |
data.usage | object · extra fields rejected | Yeswithin data | Posted Sandbox runtime usage and retained historical managed-model settlement. New BYOK Runs have no rated model-call ledger; provider usage and cost are recorded by the customer’s provider or gateway. |
data.usage.calls | array | Yeswithin data | Historical rated managed-model calls only. Empty for new BYOK Runs; does not measure native model requests. |
data.usage.calls[] | object · extra fields rejected | Yeseach array item | — |
data.usage.calls[].id | string | Yeseach array item | Resource identifier used to read or manage this resource. |
data.usage.calls[].provider | string · values: "openai" | "anthropic" | "kimi" | "zai" | "deepseek" | Yeseach array item | Native model family associated with this Harness; not a Sandpi Token supplier. |
data.usage.calls[].model | string | Yeseach array item | Exact native model ID served by your provider or gateway. |
data.usage.calls[].priceVersion | string | Yeseach array item | Historical managed-model settlement only. Immutable version of the published price captured for rating. |
data.usage.calls[].priceSourceUrl | string · format: uri | Yeseach array item | Historical managed-model settlement only. Official provider pricing reference captured with the immutable price version. |
data.usage.calls[].usageCategories | object | Yeseach array item | Historical managed-model settlement only. Provider-reported usage by category, such as uncached input, cached input and output tokens. |
data.usage.calls[].usageCategories.{key} | number | Noadditional keys | — |
data.usage.calls[].ratedLines | array | Yeseach array item | Historical managed-model settlement only. Rated accounting line items for the provider call. |
data.usage.calls[].ratedLines[] | any | Yeseach array item | — |
data.usage.calls[].upstreamAmountUsd | string | Yeseach array item | Historical managed-model settlement only. Published base-rate usage amount in USD, represented as a decimal string. |
data.usage.calls[].serviceFeeUsd | string | Yeseach array item | Historical managed-model settlement only. Itemized service fee in USD, represented as a decimal string. |
data.usage.calls[].totalAmountUsd | string | Yeseach array item | Historical managed-model settlement only. Total rated charge in USD, represented as a decimal string. |
data.usage.calls[].createdAt | string · format: date-timeValidation pattern^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$ | Yeseach array item | Creation time as a UTC date-time string. |
data.usage.calls[].serviceFeePercent | number · values: 0 | 5 | 10 | 15 | Yeseach array item | Historical managed-model settlement only. Service fee captured by this call’s immutable price version; zero for legacy calls. |
data.usage.serviceFeePercent | number · values: 0 | 5 | 10 | 15 | null | Yeswithin data | Historical managed-model fee shared by all calls, or null for mixed historical tiers. Zero for new BYOK Runs; Sandpi adds no model service fee. |
data.usage.upstreamAmountUsd | string | Yeswithin data | Historical managed-model base charges in USD. Zero for new BYOK Runs; excludes charges billed directly by your model provider. |
data.usage.serviceFeeUsd | string | Yeswithin data | Historical managed-model service fees in USD. Zero for new BYOK Runs. |
data.usage.totalAmountUsd | string | Yeswithin data | Posted Sandbox runtime plus retained historical managed-model charges in USD. Excludes model charges billed directly by your provider or gateway. |
data.usage.sandboxAmountUsd | string | Yeswithin data | Posted Sandbox runtime charge attributed to this Run. Environment initialization and file access may incur separate Environment charges. |
data.usage.sandboxRuntimeSeconds | number · ≥ 0 | Nowithin data | Authoritative billed Sandbox time attributed to this Run. |
Example
{
"data": {
"id": "",
"environmentId": "",
"sessionId": "",
"environmentSequence": -9007199254740991,
"model": "",
"input": "",
"files": [
{
"path": ""
}
],
"status": "queued",
"stopRequested": false,
"fileDiff": [
{
"path": "",
"kind": "added"
}
],
"fileDiffStatus": "pending",
"checkpoint": {
"status": "pending"
},
"createdAt": "2026-10-12T01:00:00Z",
"usage": {
"calls": [],
"serviceFeePercent": 0,
"upstreamAmountUsd": "",
"serviceFeeUsd": "",
"totalAmountUsd": "",
"sandboxAmountUsd": ""
}
}
}Error responses
400 Invalid request.application/json
401 Missing or invalid Platform API token.application/json
404 Resource not found or outside token scope.application/json
default Sandpi error.Error body application/json
Standard error envelope containing a stable code, diagnostic message and request identity.
| Field | Type and constraints | Required | Description |
|---|---|---|---|
error | object · extra fields rejected | Yes | Structured failure information. Inspect the stable code and retain the request ID for investigation. |
error.code | string | Yeswithin error | Stable error code used to distinguish failures programmatically. |
error.message | string | Yeswithin error | Human-readable diagnostic text. |
error.requestId | string | Yeswithin error | Request identifier for correlating diagnostics and support investigations. |
error.details | any | Nowithin error | Optional structured error context. Its shape is specific to the service failure. |
error.loginUrl | string | Nowithin error | Authentication URL supplied when a browser login is required. |
error.registrationOpen | boolean | Nowithin error | Whether new account registration is enabled for this deployment. |
Example
{
"error": {
"code": "platform_balance_insufficient",
"message": "Top up Platform credits before submitting a Run.",
"requestId": "req_example"
}
}Inspect the Run status and output, then its separate checkpoint, file-diff and usage state. Output can be available while checkpoint capture is still pending. Wait for the checkpoint and lifecycle lock to settle before retrieving files or forking.
Next steps
Add shared instructions, Skills or MCP tools. Use schedules for timed work and inbound webhooks for external events. See errors and retries if admission fails.