Core concepts
Forks & checkpoints
Reuse a configured workspace or a specific checkpoint.
On this page
Prepare a reusable base
Install dependencies and configure files in a base Environment. Wait until it is idle and checkpoint capture has settled. Keep secrets out of reusable workspace files: RootFS files are copied to the child.
Fork current files
Fork a managed Environment from current files or a checkpoint
/v1/environments/{environmentId}/forksAuthentication: 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. |
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/forks" \
-H "Authorization: Bearer $SANDPI_API_TOKEN" \
-H "idempotency-key: $REQUEST_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Task workspace"
}'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. |
snapshotId | string · min length 1; max length 200 | No | Sandbox0 checkpoint identifier. Use a returned initial or Run checkpoint when pinning a fork. |
Example
{
"name": "Task workspace"
}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
404 Resource not found or outside token scope.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 default mode copies the current RootFS, including file edits since the latest Run checkpoint. A Project fork stays in the source Project. Persist the idempotency key and body so an ambiguous response can be retried safely.
Fork a specific checkpoint
Supply snapshotId from an initial or Run checkpoint returned by the API. This pins the selected historical files instead of current edits. The source must be idle with no queued Run, rollback or pending checkpoint. The target still needs its own initialization to complete.
Verify the child
Read the forked Environment. Its platform.forkedFrom records the source, fork mode, optional checkpoint and source Run revision. Wait for its Environment status and initial checkpoint to be ready before submitting work.
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"
}
}What carries over
| State | Fork behavior |
|---|---|
| Selected workspace files and agents.md | Copied |
| Platform Sessions and Run history | Fresh and independent |
| Platform-managed native harness history | Independent |
| Usage and billing ledger | Independent |
| Egress credential bindings and material | Configure again |
| MCP configuration | Copied with new IDs and no credentials |
Deleting the source does not delete completed forks. Rolling back the source does not alter an independent child. See MCP, egress credentials and retention and deletion.