> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bonya.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Sandboxes

> The compute resource at the center of the Bonya API

A **sandbox** is the resource everything else in the API is scoped to — sessions, exec, files, and previews all operate on a sandbox identified by its `sandbox_id`.

## Identity

A sandbox is created from a `template_id` and gets a server-assigned `sandbox_id`, which is the only identifier every other operation (sessions, exec, files, previews) accepts. A sandbox can also have a `name`, but names are a display label, not an identifier:

* `name` is optional on create. If you omit it (or send a blank one), the service generates a friendly one.
* `name` is **not unique**. The `name` query filter on list is an exact match, and because names aren't unique it can match more than one sandbox.
* Sandboxes created before names existed as a field may have no `name` at all; every sandbox returned afterward carries one.

> [!NOTE]
>
> Always key off `sandbox_id` in code you write against this API. Treat `name` as something to show a human, not something to look a sandbox up by.

## Lifecycle states

A sandbox's `state` is one of:

| State | Meaning |
| - | - |
| `CREATING` | The sandbox is being provisioned after a create call. |
| `RUNNING` | The sandbox is up and reachable for exec, sessions, files, and previews. |
| `SUSPENDING` | The sandbox is in the process of being suspended. |
| `SUSPENDED` | The sandbox is paused. |
| `RESUMING` | The sandbox is transitioning back to `RUNNING` in response to a resume call. |
| `FAILED` | Provisioning or a lifecycle transition failed. `failure_code` and `failure_message` describe why. |

Suspension is **automatic** — there is no endpoint to request it. The only lifecycle transition you invoke explicitly is resume:

```shell cURL theme={null}
curl -s -X POST "https://api.bonya.ai/v1/sandbox/$SANDBOX_ID/resume" \
  -H "Authorization: Bearer $BONYA_API_KEY"
```

```ts TypeScript theme={null}
const resume = await sandbox.resume({ idempotencyKey: "resume-job-123" });
console.log(resume.sandboxId, resume.lifecycleOperationId, resume.alreadyRunning);
```

```python Python theme={null}
resume = sandbox.resume(idempotency_key="resume-job-123")
print(resume.sandbox_id, resume.lifecycle_operation_id, resume.already_running)
```

```go Go theme={null}
resume, err := sandbox.Resume(ctx, bonya.ResumeOptions{IdempotencyKey: "resume-job-123"})
fmt.Println(resume.SandboxID, resume.LifecycleOperationID, resume.AlreadyRunning)
```

There's no `bonya resume` CLI command — the CLI only resumes implicitly (for example, `bonya console` resumes a suspended sandbox before attaching). **MCP:** `sandbox_keepalive` wakes a paused sandbox, and `sandbox_pause` pauses one.

This synchronously resumes a suspended sandbox back to `RUNNING`. The response is a `SandboxResume` object with `sandbox_id` and `already_running`: if the sandbox was already running, the call is a no-op and `already_running` is `true`, but it still returns `200`.

> [!NOTE]
>
> There is no start, stop, wake, fork, or restore endpoint. Resume is the only explicit lifecycle verb this API exposes; every other transition happens on its own.

## Creating a sandbox

```shell cURL theme={null}
curl -s -X POST "https://api.bonya.ai/v1/sandbox?wait=ready" \
  -H "Authorization: Bearer $BONYA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"template_id": "bonya-dev"}'
```

```shell CLI theme={null}
bonya create --template bonya-dev
```

```ts TypeScript theme={null}
import { Bonya } from "@bonya-ai/bonya";

const client = new Bonya();
const sandbox = await client.createSandbox({ template: "bonya-dev" });
console.log(sandbox.id);
```

```python Python theme={null}
from bonya import Bonya

with Bonya() as client:
    sandbox = client.create_sandbox(template="bonya-dev")
    print(sandbox.id)
```

```go Go theme={null}
client, err := bonya.NewClient()
if err != nil {
    log.Fatal(err)
}
defer client.Close()

sandbox, err := client.CreateSandbox(ctx, "bonya-dev")
if err != nil {
    log.Fatal(err)
}
fmt.Println(sandbox.ID)
```

**MCP:** the `sandbox_create` tool exposes this to any connected agent.

The request body (`SandboxCreate`) can omit `template_id` when the deployment
has a configured default template. Otherwise, `template_id` is required.
Optional fields:

* `template_version` — pins a specific template version; requires `template_id`.
* `name` — display label, up to 80 characters. Not unique (see above).
* `network` — a `NetworkPolicy` controlling egress (see below).

By default, create returns **202 Accepted** with the sandbox in `CREATING` (or whatever state it's already reached) — it does not wait for the sandbox to be ready. Pass `?wait=ready` to block until the sandbox reaches a ready state, in which case the same call returns **201 Created** instead. Both responses share the same body shape: `{ "sandbox": { ...Sandbox } }`.

> [!NOTE]
>
> The REST default is non-blocking, but every SDK's `createSandbox`/`create_sandbox`/`CreateSandbox` defaults the other way — to `Wait.READY`/blocking — matching the CLI's own `--wait ready` default. Pass `wait: "none"` (or the SDK's `Wait.NONE` equivalent) explicitly to get the REST default's immediate return instead.

Create also accepts an `Idempotency-Key` header — see [Idempotency](https://docs.bonya.ai/concepts/idempotency.md) for exactly what that does and doesn't protect against.

## Getting and listing sandboxes

`GET /v1/sandbox/{sandbox_id}` returns a single sandbox as `{ "sandbox": { ...Sandbox } }`.

`GET /v1/sandbox` returns a page of sandboxes:

* `page_size` — 1 to 100, default 50.
* `page_token` — opaque cursor from a previous page's `next_page_token`.
* `states` — filter by one or more `SandboxState` values (repeat the query parameter for multiple states).
* `name` — exact-match filter; can match more than one sandbox since names aren't unique.

The response (`SandboxPage`) is `{ "sandboxes": [...], "next_page_token": "..." }`.

> [!WARNING]
>
> `DELETED` is not a filterable state. A deleted sandbox is a tombstone, not a listable state — you can't ask the list endpoint for deleted sandboxes.

## Deleting a sandbox

```shell cURL theme={null}
curl -s -X DELETE "https://api.bonya.ai/v1/sandbox/$SANDBOX_ID" \
  -H "Authorization: Bearer $BONYA_API_KEY"
```

```shell CLI theme={null}
bonya delete $SANDBOX_ID
```

```ts TypeScript theme={null}
await sandbox.delete();
```

```python Python theme={null}
sandbox.delete()
```

```go Go theme={null}
sandbox.Delete(ctx)
```

**MCP:** the `sandbox_delete` tool tears down a sandbox the same way, and is likewise idempotent.

Deletion is a **synchronous teardown** — it doesn't return until the sandbox is gone. Deleting an already-deleted (tombstoned) sandbox again returns `204`, the same as the first successful delete. An unknown `sandbox_id`, or one that belongs to a different organization than the caller's context, returns `404`.

## Network policy

A sandbox can carry a `network` policy (`NetworkPolicy`) that controls outbound network egress. Its `mode` is one of:

* `standard` (default) — the platform's normal egress behavior.
* `deny_all` — no outbound network access.
* `custom` — egress is restricted to an explicit allowlist.

When `mode` is `custom`, the policy must include an `allow` list of at least one `NetworkRule`, each with a `cidr` and an optional list of `ports` (1–65535). The `allow` list is capped at **64 rules**. For any mode other than `custom`, `allow` must be empty.

## Related

Sandboxes are the scope for everything else in the API:

* [Exec](https://docs.bonya.ai/concepts/exec.md) — run one-off commands directly against a sandbox.
* [Sessions](https://docs.bonya.ai/concepts/sessions.md) — long-lived, named command sessions inside a sandbox.
* [Files](https://docs.bonya.ai/concepts/files.md) — read, write, and manage files on a sandbox's filesystem.
* [Jobs](https://docs.bonya.ai/concepts/jobs.md) — a managed, tracked run of a command or script, optionally on a sandbox created just for it.
* [Previews](https://docs.bonya.ai/concepts/previews.md) — expose a port running inside a sandbox as an HTTPS URL.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.