# Create a sandbox

> Start an isolated Linux machine (Ubuntu) to run code, commands and services in. Paid plans only.

`POST https://api.zavu.dev/v1/sandboxes`

Start an isolated Linux machine (Ubuntu) to run code, commands and services in. **Paid plans only.**

The machine is private to this sandbox: it has no access to your Zavu account, to other sandboxes, or to anything you did not put in `env`.

**Billing**, from your balance: $0.01 when it is created, then machine time per second at the size's hourly price while it is `starting` or `running`, with a one-minute minimum per run. A sandbox that fails before it runs gets the $0.01 back. Running sandboxes are settled every five minutes, and one is stopped automatically when the balance or the monthly spend cap no longer covers 15 more minutes, when its `timeoutSeconds` passes, or when the team leaves its paid plan.

Answers `202` while it starts; poll `GET /v1/sandboxes/{sandboxId}` for `running`. Commands and file calls made while it is `starting` wait for it.

Not available with test-mode keys.

## Body

- `size` (string): Machine size, and its price per hour of machine time: - `small`: 2 vCPU, 4 GB RAM. $0.0225/h. - `default`: 4 vCPU, 8 GB RAM. $0.045/h. - `large`: 8 vCPU, 16 GB RAM. $0.09/h. One of: `small`, `default`, `large`.
- `timeoutSeconds` (integer, nullable): Stop automatically after this many seconds. Max 24 hours. `null` turns auto-stop off: the sandbox runs until you stop it. It is still stopped when the balance or the monthly spend cap no longer covers 15 more minutes, or when the team leaves its paid plan, and it is billed for every second it runs.
- `ephemeral` (boolean): Never save the disk. A stop erases it, so the sandbox cannot be resumed afterwards (`409 sandbox_expired`). Use it for one-off jobs whose results you read before stopping.
- `env` (object): Environment variables set inside the machine. Keys match `[A-Za-z_][A-Za-z0-9_]{0,127}`, at most 100 variables and 64 KB in total. A few platform names are reserved and refused with `400`.
  - `{key}` (string)
- `environment` (string): Name of one of the project's sandbox environments, created in the dashboard (Compute sandboxes › Environments). Its variables are set inside the machine; `env` wins on the same name, and the two together must stay within the `env` limits. An unknown name answers `404 not_found` and nothing is charged.
- `from` (string): Start from a saved snapshot instead of a blank machine: the `id` of one of this project's ready snapshots (`GET /v1/sandbox-snapshots`), or a share code (`zsh_` and 32 characters) another team gave you. The sandbox takes the snapshot's size, whatever `size` says. From a share code you pay this sandbox's start and machine time, and the owner keeps paying for the snapshot. An unknown id or a revoked code answers `404 not_found`; a snapshot still saving answers `409 snapshot_not_ready`. Nothing is charged in either case.
- `metadata` (object): Your own labels, returned on the sandbox. At most 40 keys; each key is 1-64 characters from `A-Z a-z 0-9 . _ : -` and starts with a letter or digit.
  - `{key}` (string)
- `idempotencyKey` (string): Retrying with the same key in this project returns `409` with the sandbox the first request created, instead of creating and charging a second one.
- `name` (string): Your label for the sandbox, returned on it. Kept by Zavu only.
- `setupScript` (string): A script run with bash once the machine is ready, in the background, as its user (passwordless sudo). It does not delay the start, and its output is not returned: write to a file if you need it. At most 64 KB.

## Responses

### 202

Accepted. The sandbox is starting.

- `sandbox` (object, required): An isolated Linux machine. It is billed per second while `starting` or `running` (with a one-minute minimum per run), plus $0.01 each time it is created or resumed. A stopped sandbox costs nothing.
  - `id` (string, required)
  - `name` (string, nullable): Your label for it, set with `name` on create. Copies keep their source's name.
  - `status` (string, required): - `starting`: accepted, the machine is being prepared. Commands and file calls wait for it. - `running`: the machine is up. Machine time is billed. - `stopping`: a stop was requested; billing already ended. - `stopped`: the disk is kept until `expiresAt`, and `POST /resume` starts it again. Not billed. - `failed`: the machine could not start or failed on its host. See `failureReason`. A sandbox that fails before it ever runs gets its start fee back. One of: `starting`, `running`, `stopping`, `stopped`, `failed`.
  - `size` (string, required): Machine size, and its price per hour of machine time: - `small`: 2 vCPU, 4 GB RAM. $0.0225/h. - `default`: 4 vCPU, 8 GB RAM. $0.045/h. - `large`: 8 vCPU, 16 GB RAM. $0.09/h. One of: `small`, `default`, `large`.
  - `timeoutSeconds` (integer, required, nullable): The sandbox is stopped automatically this many seconds after it started (or was last resumed). Checked every minute. `null` when auto-stop is off: it runs until you stop it, or until the balance, the spend cap or leaving the paid plan stops it.
  - `ephemeral` (boolean, required): Created with `ephemeral: true`. Its disk is never saved: a stop erases it, `expiresAt` is the moment it stopped, and resume answers `409 sandbox_expired`.
  - `metadata` (object, required): What you attached when creating it. Kept by Zavu only; never visible inside the machine.
    - `{key}` (string)
  - `startedAt` (string<date-time>, nullable): When the current or last run was accepted.
  - `stoppedAt` (string<date-time>, nullable): When the last run ended. Null while starting or running.
  - `expiresAt` (string<date-time>, nullable): Present while `stopped`: when the disk is deleted, 14 days after the stop, or the stop itself for an ephemeral sandbox. After it, resume answers `409 sandbox_expired`.
  - `stopReason` (string, required, nullable): Why the last run ended, while `stopping` or `stopped`; null otherwise. `requested`: you stopped it, from the API or the dashboard. `timeout`: its `timeoutSeconds` passed. `insufficient_balance` and `spend_cap_reached`: the balance or the monthly spend cap no longer covered 15 more minutes. `plan`: the team left its paid plan. `provider`: it ended on its host. The `sandbox.stopped` project webhook carries the same value. One of: `requested`, `timeout`, `insufficient_balance`, `spend_cap_reached`, `plan`, `provider`, `null`.
  - `costTotal` (number, required): USD charged for this sandbox so far: start fees plus machine time. Machine time of a running sandbox is settled every five minutes, so this lags by up to five minutes until it stops.
  - `failureReason` (string, nullable): Why it failed. Present only when `status` is `failed`.
  - `forkedFromSandboxId` (string, nullable): The sandbox this one is a copy of, when it was made with `POST /v1/sandboxes/{sandboxId}/fork`.
  - `fromSnapshotId` (string, nullable): The project's saved snapshot it started from. Null when it started blank, as a copy, or from another team's shared snapshot.
  - `createdAt` (string<date-time>, required)
  - `updatedAt` (string<date-time>, required)

### 400

Invalid body, or the key is a test-mode key: sandboxes need a live key.

- `code` (string, required)
- `message` (string, required)
- `details` (object)

### 401

Unauthorized.

- `code` (string, required)
- `message` (string, required)
- `details` (object)

### 402

`paid_plan_required`: sandboxes need a paid plan, and Free cannot start one (`details.dashboardUrl` is `/upgrade`). `insufficient_balance`: the balance does not cover the $0.01 start fee plus 15 minutes of machine time at the sandbox's size. `spend_cap_reached`: the monthly spend cap does not leave that much room.

- `code` (string, required)
- `message` (string, required)
- `details` (object)

### 403

`plan_limit_reached`: the team already has 10 sandboxes starting or running. `details.limit` and `details.running` say so.

- `code` (string, required)
- `message` (string, required)
- `details` (object)

### 404

`not_found`: `environment` names no environment in this project, or `from` is not a share code of a ready snapshot (it was revoked, or the snapshot deleted). Nothing was charged.

- `code` (string, required)
- `message` (string, required)
- `details` (object)

### 409

An idempotent replay: the `idempotencyKey` was already used in this project. Nothing was created or charged; the body is the sandbox the first request created, in its current state.

- `sandbox` (object, required): An isolated Linux machine. It is billed per second while `starting` or `running` (with a one-minute minimum per run), plus $0.01 each time it is created or resumed. A stopped sandbox costs nothing.
  - `id` (string, required)
  - `name` (string, nullable): Your label for it, set with `name` on create. Copies keep their source's name.
  - `status` (string, required): - `starting`: accepted, the machine is being prepared. Commands and file calls wait for it. - `running`: the machine is up. Machine time is billed. - `stopping`: a stop was requested; billing already ended. - `stopped`: the disk is kept until `expiresAt`, and `POST /resume` starts it again. Not billed. - `failed`: the machine could not start or failed on its host. See `failureReason`. A sandbox that fails before it ever runs gets its start fee back. One of: `starting`, `running`, `stopping`, `stopped`, `failed`.
  - `size` (string, required): Machine size, and its price per hour of machine time: - `small`: 2 vCPU, 4 GB RAM. $0.0225/h. - `default`: 4 vCPU, 8 GB RAM. $0.045/h. - `large`: 8 vCPU, 16 GB RAM. $0.09/h. One of: `small`, `default`, `large`.
  - `timeoutSeconds` (integer, required, nullable): The sandbox is stopped automatically this many seconds after it started (or was last resumed). Checked every minute. `null` when auto-stop is off: it runs until you stop it, or until the balance, the spend cap or leaving the paid plan stops it.
  - `ephemeral` (boolean, required): Created with `ephemeral: true`. Its disk is never saved: a stop erases it, `expiresAt` is the moment it stopped, and resume answers `409 sandbox_expired`.
  - `metadata` (object, required): What you attached when creating it. Kept by Zavu only; never visible inside the machine.
    - `{key}` (string)
  - `startedAt` (string<date-time>, nullable): When the current or last run was accepted.
  - `stoppedAt` (string<date-time>, nullable): When the last run ended. Null while starting or running.
  - `expiresAt` (string<date-time>, nullable): Present while `stopped`: when the disk is deleted, 14 days after the stop, or the stop itself for an ephemeral sandbox. After it, resume answers `409 sandbox_expired`.
  - `stopReason` (string, required, nullable): Why the last run ended, while `stopping` or `stopped`; null otherwise. `requested`: you stopped it, from the API or the dashboard. `timeout`: its `timeoutSeconds` passed. `insufficient_balance` and `spend_cap_reached`: the balance or the monthly spend cap no longer covered 15 more minutes. `plan`: the team left its paid plan. `provider`: it ended on its host. The `sandbox.stopped` project webhook carries the same value. One of: `requested`, `timeout`, `insufficient_balance`, `spend_cap_reached`, `plan`, `provider`, `null`.
  - `costTotal` (number, required): USD charged for this sandbox so far: start fees plus machine time. Machine time of a running sandbox is settled every five minutes, so this lags by up to five minutes until it stops.
  - `failureReason` (string, nullable): Why it failed. Present only when `status` is `failed`.
  - `forkedFromSandboxId` (string, nullable): The sandbox this one is a copy of, when it was made with `POST /v1/sandboxes/{sandboxId}/fork`.
  - `fromSnapshotId` (string, nullable): The project's saved snapshot it started from. Null when it started blank, as a copy, or from another team's shared snapshot.
  - `createdAt` (string<date-time>, required)
  - `updatedAt` (string<date-time>, required)

### 429

`daily_limit_exceeded`: the team started 200 sandboxes today (creates and resumes both count). Resets at 00:00 UTC.

- `code` (string, required)
- `message` (string, required)
- `details` (object)

### 503

`sandbox_unavailable`: no machine capacity right now. Nothing was charged for machine time; retry shortly.

- `code` (string, required)
- `message` (string, required)
- `details` (object)

## Example

```bash
curl -X POST "https://api.zavu.dev/v1/sandboxes" \
  -H "Authorization: Bearer $ZAVUDEV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "size": "default",
  "timeoutSeconds": 1800,
  "env": {
    "API_URL": "https://api.example.com"
  },
  "metadata": {
    "job": "build-1042"
  }
}'
```

```typescript
import Zavudev from "@zavudev/sdk";

const zavu = new Zavudev({ apiKey: process.env.ZAVUDEV_API_KEY });

const result = await zavu.sandboxes.create({
  size: "default",
  timeoutSeconds: 1800,
  env: {
    API_URL: "https://api.example.com",
  },
  metadata: {
    job: "build-1042",
  },
});

console.log(result);
```
