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

# Usage

> Sandboxes, exec, sessions, files, previews, snapshots, jobs, templates, and secrets from TypeScript

## What you can do

| I want to… | Call |
| - | - |
| Start a sandbox | `client.createSandbox({ template, image, snapshot, gpu, network, env, secretSets })` |
| Reconnect to one | `client.getSandbox(id)` / `.getSandboxByName(name)` |
| Find my sandboxes | `client.listSandboxes({ states, name })` |
| Run a command | `sandbox.exec(cmd)` |
| Stream output live | `sandbox.execStream(cmd)` |
| Keep a process running after I disconnect | `sandbox.createSession(name, cmd)` / `.attachSession(name)` |
| Move files in and out | `sandbox.readFile` / `.writeFile` / `.uploadFile` / `.downloadFile` / `.listFiles` / `.statFile` / `.mkdirFile` / `.removeFile` / `.moveFile` |
| Expose a port to the web | `sandbox.createPreview(port)` |
| Capture disk state | `sandbox.snapshot()` / `.listSnapshots()` |
| Clone a running sandbox | `sandbox.fork(count)` |
| Pause or keep awake | `sandbox.pause("warm" \| "cold")` / `.keepalive(seconds)` / `.resume()` |
| Restrict egress | `sandbox.updateNetwork({ mode: "allowlist", allow: [{ domain: "pypi.org" }] })` |
| Run a managed or scheduled job | `client.runJob(spec)` / `client.createJobSchedule(schedule, job)` |
| See GPU capacity | `client.listGpuOptions()` |
| Save a launch configuration | `client.createTemplate({ name, config })` |
| Pull private images | `client.createRegistryConnection(...)` |
| Keep credentials out of code | `client.createSecret(name, value)` / `client.createSecretSet({ name, values })` |
| Switch organization | `client.listOrganizations()` / `client.organizationId = id` |

Everything is a **flat method** on `Bonya` or `Sandbox`. There is no
`client.sandboxes.create(...)` namespace to navigate.

> [!NOTE]
>
> `Bonya` also has convenience methods that take a sandbox id instead of a
> `Sandbox` handle, such as `client.createSession(sandboxId, name, cmd)` and
> `client.createPreview(sandboxId, port)`. Each fetches the sandbox first, so
> prefer calling on `sandbox` when you already hold one.

## Create sandboxes

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

const client = new Bonya();
const sandbox = await client.createSandbox({
  template: "bonya-dev",
  wait: Wait.READY,
  idempotencyKey: "create-job-123",
});
```

| Option | Type | Description |
| - | - | - |
| `template` | `string` | Template id. Omit for the deployment default. |
| `templateRevision` | `number` | Pin a template revision. |
| `image` | `string` | Start from a public OCI image instead of a template. |
| `snapshot` | `string` | Start from a snapshot id. |
| `gpu` | `{ type, count }` | Requires `image` (or an image template). See `listGpuOptions()`. |
| `network` | `NetworkPolicy` | `open`, `allowlist` or `airgap`. |
| `env` | `Record<string, string>` | Environment variables. |
| `secretSets` | `string[]` | Secret set **names** to inject. |
| `name` | `string` | Display name. Names are not unique. |
| `wait` | `Wait \| "ready" \| "none"` | Defaults to `Wait.READY`. |
| `idempotencyKey` | `string` | Generated and reused across retries if omitted. |

If create exhausts its deadline the SDK throws `SandboxCreationTimeoutError`,
which carries the `idempotencyKey` so you can retry the same creation instead
of starting a second sandbox.

## Get, list, and find by name

```ts theme={null}
const sandbox = await client.getSandbox("sb-abc123");
const byName = await client.getSandboxByName("scratch");

for await (const item of client.listSandboxes({ states: ["running", "warm"] })) {
  console.log(item.id, item.state);
}

await sandbox.refresh(); // update the handle's fields
```

`sandbox.state` is one of `creating`, `running`, `cooling`, `warm`, `cold`,
`waking`, `failed`, `deleting`, `deleted` or `lost`.

## Run commands

```ts theme={null}
const result = await sandbox.exec(["bash", "-lc", "uname -a"], {
  cwd: "/workspace",
  timeout: 30,
  check: true, // throw ExecFailedError on a non-zero exit
});
console.log(result.exitCode, result.stdout, result.stderr);
```

### Streaming exec

```ts theme={null}
import { Exit, Stderr, Stdout } from "@bonya-ai/bonya";

const session = sandbox.execStream(["bash", "-lc", "echo out; echo err >&2"]);
try {
  for await (const event of session) {
    if (event instanceof Stdout) process.stdout.write(Buffer.from(event.data));
    else if (event instanceof Stderr) process.stderr.write(Buffer.from(event.data));
    else if (event instanceof Exit) console.log("exit:", event.exitCode);
  }
} finally {
  session.close();
}
```

The process dies when the stream closes. For anything that must outlive your
connection, use a session.

## Persistent sessions

```ts theme={null}
await sandbox.createSession("server", ["bash"]);

const stream = await sandbox.attachSession("server");
await stream.write("npm run dev\n");
for await (const event of stream) {
  // Stdout, Exit, SessionEnded, SessionOutputDropped
}
await stream.detach(); // the process keeps running

console.log(await sandbox.listSessions());
await sandbox.killSession("server");
```

Session names match `^[a-z][a-z0-9-]{0,31}$`. A session keeps running after you
detach and replays what it produced while nobody was attached.

## Files

```ts theme={null}
await sandbox.writeFile("/workspace/hello.txt", "hello\n");
const data = await sandbox.readFile("/workspace/hello.txt"); // Uint8Array

await sandbox.mkdirFile("/workspace/out");
await sandbox.moveFile("/workspace/hello.txt", "/workspace/out/hello.txt");

for (const entry of await sandbox.listFiles("/workspace/out")) {
  console.log(entry.name, entry.size);
}

await sandbox.uploadFile("./local.json", "/workspace/out/local.json");
await sandbox.downloadFile("/workspace/out/local.json", "./copy.json");
await sandbox.removeFile("/workspace/out", true); // recursive
```

## Previews

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

await sandbox.createSession("web", ["python3", "-m", "http.server", "3000"]);

// Ports must be 1024-65535. PRIVATE is the default.
const preview = await sandbox.createPreview(3000);
console.log(preview.url);

// A private preview is opened with a one-time grant token. This returns the
// single-use entry point; open it once and let the cookie carry the session.
console.log(sandbox.previewBrowserUrl(preview));

const open = await sandbox.createPreview(8080, { auth: PreviewAuth.PUBLIC, ttlSeconds: 3600 });

const grant = await sandbox.createPreviewGrant(preview.id, { ttlSeconds: 600 });
await sandbox.renewPreviewGrant(preview.id, grant.id);
await sandbox.revokePreviewGrant(preview.id, grant.id);

await sandbox.deletePreview(preview.id);
```

Grant tokens are shown once, in the response that creates them.

## Lifecycle, snapshots, and forks

```ts theme={null}
await sandbox.pause("warm");          // or "cold"
await sandbox.keepalive(30 * 60);     // keep awake; also wakes a paused sandbox
await sandbox.resume();               // wakes via keepalive

await sandbox.updateNetwork({
  mode: "allowlist",
  allow: [{ domain: "pypi.org" }, { cidr: "10.0.0.0/8" }],
});

const snapshot = await sandbox.snapshot({ name: "baseline" });
const restored = await client.createSandbox({ snapshot: snapshot.id });
const { sandboxes } = await sandbox.fork(2);
await snapshot.delete();
```

## Jobs

```ts theme={null}
const run = await client.runJob({
  newSandbox: {},
  cmd: ["./run-tests.sh"],
  commandTimeoutSeconds: 600,
});
console.log(run.status, run.result?.exitCode);
```

`runJob` waits for the result; `startJob` returns immediately. Use
`getJobRun(id)`, `listJobRuns()` and `cancelJobRun(id)` to track runs.

### Schedules

```ts theme={null}
const schedule = await client.createJobSchedule(
  { cronExpressions: ["0 3 * * *"], timeZone: "UTC" },
  { newSandbox: {}, cmd: ["./nightly.sh"] },
);

await client.setJobSchedulePaused(schedule.scheduleId, true);
await client.triggerJobSchedule(schedule.scheduleId);
await client.deleteJobSchedule(schedule.scheduleId);
```

A schedule uses exactly one of `cronExpressions`, `intervalSeconds` or
`runAtUnixNanos`.

## Templates

```ts theme={null}
const template = await client.createTemplate({
  name: "node-dev",
  config: { image: "node:22", cpu: 2, memoryMib: 4096, env: { NODE_ENV: "development" } },
});

await client.updateTemplate(template.id, { config: { image: "node:22", cpu: 4 } }); // new revision
console.log(await client.listTemplates());
await client.deleteTemplate(template.id);
```

## Registry connections, secrets, and secret sets

```ts theme={null}
const conn = await client.createRegistryConnection({
  name: "ghcr",
  registry: "ghcr.io",
  username: "me",
  password: "<token>",
});
await client.validateRegistryConnection(conn.id, "registry.example.com/team/app:latest");

await client.createSecret("DB_PASSWORD", "s3cret");
await client.createSecretSet({ name: "app-env", values: { API_URL: "https://example.com" } });

const sandbox2 = await client.createSandbox({ secretSets: ["app-env"] });
```

Secret and secret-set values are **write-only**; the API never returns them.
Reference secret sets by **name**.

## Organizations

```ts theme={null}
for (const org of await client.listOrganizations()) console.log(org.id, org.name);
client.organizationId = "org_123"; // sent as X-Bonya-Organization-ID
```

The precedence is the constructor option, then `BONYA_ORGANIZATION_ID`, then
your personal organization. Creating organizations, members, invites and API
keys is done in the dashboard, not through the SDK.

## Errors

Every error extends `BonyaError` (with `sandboxId`, `idempotencyKey`, `code`).
Branch on the class: `AuthenticationError`, `SandboxNotFoundError`,
`SandboxCreationTimeoutError`, `ExecFailedError`, `TimeoutError`,
`NoCapacityError`, `RateLimitedError`, `OutcomeUnknownError`,
`RemoteFileNotFoundError`, and so on. API keys and capability tokens are
redacted from error messages.

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

try {
  await client.getSandbox("sb-missing");
} catch (error) {
  if (error instanceof SandboxNotFoundError) console.log("gone");
  else throw error;
}
```

Server errors are RFC 9457 `problem+json`; see [Errors](https://docs.bonya.ai/concepts/errors.md).


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