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

# Run Your First Sandbox

> Create a sandbox, run echo hello in it, and destroy it, from Python, TypeScript, the CLI, or HTTP.

Run `echo hello` in a remote sandbox, then destroy it. BoxRouter chooses a route, which is a provider and the account that pays for it.

**Before You Begin:** A BoxRouter API key from your [dashboard](https://boxrouter.ai/dashboard), and either a connected provider key or Credits in the same workspace. [Set Up Your Account](https://boxrouter.ai/docs/setup.md) covers both.

| Approach | Best For |
| - | - |
| **Python** or **TypeScript** | Sandboxes inside your own application |
| **CLI** | Scripts and one-off commands |
| **HTTP** | Any other language, with `curl` and `jq` |

Use Python if you have no preference.

## Set Your API Key

Clients read `BOXROUTER_API_KEY`. Set `BOXROUTER_BASE_URL` to override `https://api.boxrouter.ai/api/v1`.

The hidden prompt keeps the key out of your shell history.

```bash theme={"theme":"css-variables"}
printf 'BoxRouter API key: '; read -rs BOXROUTER_API_KEY && export BOXROUTER_API_KEY; printf '\n'
```

## Install a Client

```bash Python theme={"theme":"css-variables"}
python -m pip install boxrouter
```

```bash TypeScript theme={"theme":"css-variables"}
npm install @boxrouter/sdk
```

```bash CLI theme={"theme":"css-variables"}
# macOS and Linux
curl -fsSL https://boxrouter.ai/install | sh
# Windows, in PowerShell
irm https://boxrouter.ai/install.ps1 | iex
```

The Python SDK needs Python 3.11 or later; the CLI provides Python 3.11 to 3.14. The TypeScript SDK supports
Node.js 22.18 or later. This `.mts` example uses `await using`, which needs Node.js 24 or later. HTTP needs curl 7.76 or later and `jq`.

## Run a Command and Clean Up

Save the Python example as `quickstart.py` and run `python quickstart.py`, or the TypeScript example as
`quickstart.mts` and run `node quickstart.mts`. Paste the CLI and HTTP examples into a shell.

```python Python theme={"theme":"css-variables"}
from boxrouter import BoxRouter

with BoxRouter() as client:
    with client.sandboxes.create() as box:
        print("sandbox:", box.id, flush=True)
        print(box.exec("echo hello", check=True).stdout, end="")
    print("state:", box.state)  # the with block's destroy returned the sandbox
```

```typescript TypeScript theme={"theme":"css-variables"}
import { BoxRouter } from '@boxrouter/sdk';
await using client = new BoxRouter();
await using box = await client.sandboxes.create();
console.log('sandbox:', box.id);
const result = await box.exec('echo hello', { check: true });
process.stdout.write(result.stdout);
```

```bash CLI theme={"theme":"css-variables"}
boxrouter run 'echo hello'
```

```bash HTTP theme={"theme":"css-variables"}
(
  set -euo pipefail
  API=${BOXROUTER_BASE_URL:-https://api.boxrouter.ai/api/v1}
  AUTH="Authorization: Bearer $BOXROUTER_API_KEY"
  id=$(curl -sS --fail-with-body -X POST "$API/sandboxes" -H "$AUTH" | jq -er '.id')
  echo "sandbox: $id"
  cleanup() {
    curl -sSf -X DELETE "$API/sandboxes/$id" -H "$AUTH" | jq -r '"state: " + .state'
  }
  trap cleanup EXIT
  curl -sS --fail-with-body "$API/sandboxes/$id/commands" -H "$AUTH" \
    -H "Content-Type: application/json" -d '{"command": "echo hello"}' |
    jq -ej 'if .exit_code == 0 then .stdout else error("Command exited with \(.exit_code)") end'
)
```

Output

```text Python theme={"theme":"css-variables"}
sandbox: <SANDBOX_ID>
hello
state: destroyed
```

```text TypeScript theme={"theme":"css-variables"}
sandbox: <SANDBOX_ID>
hello
```

```text CLI theme={"theme":"css-variables"}
hello
```

```text HTTP theme={"theme":"css-variables"}
sandbox: <SANDBOX_ID>
hello
state: destroyed
```

Every example requests deletion even when the command fails. The Python example prints the state the destroy
returned. `run` requests deletion when the command exits, whatever its status, and the HTTP example
deletes in an `EXIT` trap. `DELETE` answers `202` with the sandbox. Its state is `destroyed` once deletion is
confirmed and `destroying` while cleanup is pending.

If the state is `destroying`, deletion is still pending. Use the sandbox ID the Python, TypeScript, and HTTP examples print to
[check its state and recover](https://boxrouter.ai/docs/errors.md#when-cleanup-fails) before retrying. `boxrouter run` prints no sandbox ID; create the
sandbox with `boxrouter create` when you need one.

## What Happened

A route is a provider and the account that pays for it: your provider key or Credits. Workspace
restrictions and the default request filter routes; BoxRouter tries the top-ranked eligible route
first. The sandbox's `route` field records the choice; [routing](https://boxrouter.ai/docs/routing.md) explains the ranking.

If your program stops, the sandbox keeps running until its lifetime ends (300 seconds unless you set `lifetime_seconds`),
and can run past the end of its lifetime. Check its state and destroy it when you no longer need it. See [Lifecycle](https://boxrouter.ai/docs/lifecycle.md).

When a command fails inside the sandbox, `exec` returns its `exit_code`, `stdout` and `stderr` and raises no API
error. These examples pass `check=True` (`{ check: true }` in TypeScript).
That raises `CommandFailed` for a nonzero exit or a timeout.

## If It Fails

| Error | Cause and Fix |
| - | - |
| [`401 unauthenticated`](https://boxrouter.ai/docs/error-codes#unauthenticated) | `BOXROUTER_API_KEY` is unset, mistyped, or revoked. Set it again from **API Keys**. |
| [`409 no_eligible_route`](https://boxrouter.ai/docs/error-codes#no_eligible_route) | No route in your workspace can take the sandbox. Connect a provider key or buy Credits; the error's `fix` names what is missing. |

If sandbox creation returns either error, it created nothing. If a later call fails, a sandbox
created earlier may still need cleanup. [Check its state](https://boxrouter.ai/docs/errors.md#when-cleanup-fails) before retrying.

## Next Steps

> [card] [Create a Sandbox](https://boxrouter.ai/docs/sandboxes.md)
>
> Keep a sandbox across calls and say what it needs.

> [card] [Work with Files](https://boxrouter.ai/docs/files.md)
>
> Upload a file, process it, and download the result.

> [card] [Recover from Errors](https://boxrouter.ai/docs/errors.md)
>
> Tell whether your work started, then retry without duplicates.


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