Skip to main content
The boxrouter command runs one-off commands in a temporary sandbox, keeps a sandbox across several commands, and wraps every API operation. Use its output in shell scripts: create prints an ID, read writes raw bytes, and api prints JSON.

Install

The installer sets up uv and a supported Python if you have neither, and installs the Python SDK as a dependency. The CLI supports Python 3.11 to 3.14. Run the same command again to upgrade. To remove the CLI, run the installer with --uninstall: curl -fsSL https://boxrouter.ai/install | sh -s -- --uninstall, or in PowerShell, & ([scriptblock]::Create((irm https://boxrouter.ai/install.ps1))) -Uninstall.
It reads BOXROUTER_API_KEY from the environment and sends requests to https://api.boxrouter.ai/api/v1. Set BOXROUTER_BASE_URL or pass --base-url to use another deployment. Set Up Your Account covers the key and how compute is paid for.

Update Notice

In an interactive terminal, the CLI may print one line on stderr after a command, such as Update available 0.6.117 -> 0.6.118. Run: curl -fsSL https://boxrouter.ai/install | sh. The upgrade command matches how you installed it. A CLI installed with the installer upgrades by running the installer again, because uv tool upgrade cannot move a tool installed from release files. Other installs get a pointer to this page. The check runs at most once every 24 hours in a background process, so no command waits for it. It sends one anonymous HTTPS request to BoxRouter (https://boxrouter.ai/cli/latest.txt), a text file with the newest stable version. BoxRouter sees your IP address and the HTTP client’s user agent, and the request includes no account, key, command, or version. Nothing is sent to PyPI. Failures are silent. The line never appears when stdout or stderr is not a terminal, when CI is set, or with --json-errors. Set BOXROUTER_NO_UPDATE_CHECK=1 to turn it off for a session, or put update_check = false in ~/.config/boxrouter/config.toml to turn it off for good.

Run One Command

run creates a sandbox, runs the command, and requests deletion when the command exits. It copies the command’s stdout and stderr to yours and exits with the command’s status. A status outside 0 to 255 becomes 1, and the CLI prints Remote exit code: N to stderr. A nonzero status can also mean a client or API error, so read stderr first. To keep the sandbox ID for checking cleanup, create the sandbox yourself as shown in the next section.

Keep a Sandbox Between Commands

create prints the sandbox ID and nothing else. Pass that ID to the other commands. read writes raw bytes to stdout, so redirect it to save a binary file. write takes --body from a file, or from stdin with -, and fails above 1 MiB with 413 resource_limit. Use a Volume for larger files. The script verifies a file round trip and destroys the sandbox on exit:
If the response to create is lost, the sandbox may exist without an ID. Pass your own idempotency key and a label; the CLI generates a new key for each call. Resend the same call to get the same sandbox back, or find it by label. Recover from Errors has the rules. This Bash example needs python3 on your PATH to make a unique key. It prints the key before creating the sandbox, lists matching sandboxes, and requests deletion on exit. If creation fails without an ID, use the printed key to retry the same request or find the sandbox by its run label.

Say What the Sandbox Needs

create and run take the same options. Repeat --providers to limit eligible providers to those IDs in the listed order; workspace restrictions can preserve a different order. Repeat --require NAME for each requirement that needs no settings, as in --require pause_resume --require terminal. Pass everything else as JSON: --requirements for a requirement with settings, --resources, --preferences, --lifetime-seconds and --image. --require and --requirements cannot be combined. A JSON value is written inline, as @FILE, or as - for stdin. The CLI rejects a misspelt field before sending any request and lists the fields the option accepts. boxrouter create --help shows each shape. To see what a request would match before you create anything, use boxrouter match. It takes the same options except --providers; use --preferences '{"only":["<PROVIDER_ID>"]}' instead. It creates nothing. For each eligible route it prints the rank, the reason, and the hourly list price, and it lists each excluded route and why. Routing explains the ranking, and provider support lists what each route offers. A name in preferences.only that is not a provider ID fails with 400 unrecognised_word and creates nothing; details.known lists the valid names. To let BoxRouter select from all eligible providers, omit --providers or preferences.only. --timeout-seconds is the command’s deadline, timeout_seconds in the API. At the deadline the command is ended, its output so far is printed, and the CLI exits with status 124. Run commands says what the deadline stops; the sandbox’s lifetime is a separate clock (Lifecycle).

Attach a Volume

boxrouter volumes create NAME creates a volume and prints it as JSON; repeat --label KEY=VALUE to tag it. create and match take --volume as the volume ID, as ID:ACCESS with read, write, shared or branch, as ID@CHECKPOINT to start from a saved checkpoint, or as ID@CHECKPOINT:ACCESS. JSON still works, and is the way to set path.
Volumes covers access modes, checkpoints, and uploads.

Read Errors

An API or transport error exits with 1, and an invalid option value exits with 2. The message names the request ID, the operation ID if one exists, the outcome, the Retry value, and a fix. When a request carried an idempotency key, the message prints it too. The CLI resends a Retry: safe error twice by itself, with the same key, before it reports the error. Keep these values: you need them to check whether the work started before you retry. Pass --json-errors to get the same fields as one JSON object on stderr, which jq can read. --show-response-headers writes the HTTP status and headers to stderr, including x-request-id. Repeat a failed call only as Retry allows; Recover from Errors says what each value means.

Call Any API Operation

boxrouter api GROUP OPERATION runs any operation in the API reference and prints the JSON response. Path parameters are positional arguments, query parameters are options, and a request body is --body, written like the JSON options above.
These operations have no dedicated command; call them through boxrouter api:

Open a Terminal

This opens an interactive shell on routes that support terminals. Leaving the shell leaves the sandbox running, so destroy it with boxrouter destroy ID when you finish. Connect covers terminals and previews.

Next Steps

Find an SDK or CLI Call

The CLI command for each sandbox task.

Connect to a Sandbox

Terminals and previews.

Recover from Errors

When resending a failed call is safe.