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

# Call the BoxRouter API

> Authenticate a BoxRouter API request, follow the sandbox workflow, retry a mutation safely, and find the endpoint you need.

Create a sandbox, use its BoxRouter ID to run commands and move files, then destroy it. The Python
and TypeScript SDKs and the [CLI](https://boxrouter.ai/docs/cli.md) wrap the same calls.

## Authenticate

Send a BoxRouter API key as a bearer token to `https://api.boxrouter.ai/api/v1`. The key never
carries provider credentials; [set up your account](https://boxrouter.ai/docs/setup.md) covers creating one and its permissions.

```http theme={"theme":"css-variables"}
Authorization: Bearer <BOXROUTER_API_KEY>
```

A missing, mistyped, or revoked bearer key returns `401 unauthenticated`. For `403 forbidden`, read
`message` and any `fix`: the key may lack permission or another access check may have refused the request.

## Read an Error

An error response is one JSON object. Branch on `error.retry`, `error.code` and `error.outcome`, never on `error.message`.

```json theme={"theme":"css-variables"}
{
  "error": {
    "code": "not_found",
    "message": "No BoxRouter API operation has this method and path.",
    "outcome": "not_started",
    "retry": "check_outcome",
    "retry_after_seconds": null,
    "request_id": "<REQUEST_ID>",
    "fix": "Check the method and path against the API reference; every path starts with /api/v1."
  }
}
```

`code`, `message`, `outcome`, `retry`, `retry_after_seconds`, and `request_id` are always present. `fix`, `operation_id`, `details`, and the
provider's own `native_*` fields appear when they apply. [Recover from Errors](https://boxrouter.ai/docs/errors.md) describes each field and
each status. [Error codes](https://boxrouter.ai/docs/error-codes.md) lists every code.

`error.retry` says whether to send the call again: `safe` (wait `retry_after_seconds`, then resend with the same
`Idempotency-Key`), `after_fix` (change what `fix` names first), `check_outcome` (read `operation_id` first) or `never`. The HTTP
status never decides a retry. A `2xx` succeeded, and a `202` is a success status, not a completion: read the resource
or `operation_id` after a `202`.

## Follow the Sandbox Workflow

| Step | Endpoint | Result |
| - | - | - |
| Create | [`POST /sandboxes`](https://boxrouter.ai/docs/reference/create) | The sandbox and its ID |
| Run a command | [`POST /sandboxes/{sandbox_id}/commands`](https://boxrouter.ai/docs/reference/exec) | `stdout`, `stderr` and `exit_code` |
| Write a file | [`PUT /sandboxes/{sandbox_id}/files/content`](https://boxrouter.ai/docs/reference/write) | `204` |
| Read a file | [`GET /sandboxes/{sandbox_id}/files/content`](https://boxrouter.ai/docs/reference/read) | The file's bytes |
| Inspect | [`GET /sandboxes/{sandbox_id}`](https://boxrouter.ai/docs/reference/get) | State, lifetime, route, and cost |
| Destroy | [`DELETE /sandboxes/{sandbox_id}`](https://boxrouter.ai/docs/reference/destroy) | `202` with the sandbox: `destroyed`, or `destroying` with an `operation_id` |

[`POST /sandboxes/match`](https://boxrouter.ai/docs/reference/match) takes a create request body and returns the routes that would
qualify without creating anything. The [quickstart](https://boxrouter.ai/docs/quickstart.md) runs this workflow with `curl`.

## Retry a Mutation Safely

Send an `Idempotency-Key` header, 1 to 128 characters, with each sandbox creation, command, and file
write. Save it before you send the request. The SDKs and the CLI send a key for you and reuse it when they
resend a `retry: safe` error.

A repeat with the same key and identical body returns the recorded result when available. While the
work is unresolved, it returns `409`; it does not run the work again. A different body returns
`409 conflict`.

The example needs `curl`, `jq`, and `openssl`. It creates the same sandbox twice with one key.

```bash theme={"theme":"css-variables"}
(
  set -euo pipefail
  API=${BOXROUTER_BASE_URL:-https://api.boxrouter.ai/api/v1}
  AUTH="Authorization: Bearer $BOXROUTER_API_KEY"
  key=$(openssl rand -hex 16)
  create() {
    curl -sSf -X POST "$API/sandboxes" -H "$AUTH" -H "Idempotency-Key: $key" \
      -H "Content-Type: application/json" -d '{"lifetime_seconds": 300}' | jq -r .id
  }
  id=$(create)
  trap 'curl -sSf -o /dev/null -X DELETE "$API/sandboxes/$id" -H "$AUTH"' EXIT
  echo "$id"
  [[ "$(create)" == "$id" ]] && echo "same sandbox"
)
```

Output

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

The second create returned the recorded result instead of creating another sandbox. If a response is lost,
save the key before you send the request and use it to
[recover the creation](https://boxrouter.ai/docs/errors.md#recover-a-lost-creation-response).

A key replays its recorded result for at least 24 hours after the request; that is all the API contract promises.
BoxRouter keeps resolved records longer, up to 90 days under its [retention policy](https://boxrouter.ai/docs/data-retention.md#what-boxrouter-keeps),
but do not rely on it. Once a record is removed, the same key returns `409 conflict` and runs nothing, so use a new key
for new work.

For unresolved work, the operation record stays until it resolves, and a retry returns `409` with its
`operation_id`. `DELETE` takes no key.

A replayed key returns BoxRouter's recorded result, but after a lost response the provider may already
have run the work. [Recover from Errors](https://boxrouter.ai/docs/errors.md) says what to check before you resend.

## Clean Up in a Script

A script that creates a sandbox has to request deletion on every path. `DELETE` always answers `202` with the
sandbox, so this script prints its `state`. It needs `curl` and `jq`. While deletion is pending the state is
`destroying` and the body holds an `operation_id`; [Recover from Errors](https://boxrouter.ai/docs/errors.md#when-cleanup-fails) says what to do with it.

```bash 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 -sSf -X POST "$API/sandboxes" -H "$AUTH" | jq -r .id)
  trap 'curl -sSf -X DELETE "$API/sandboxes/$id" -H "$AUTH" | jq -r .state' EXIT
  curl -sSf "$API/sandboxes/$id/commands" -H "$AUTH" -H "Content-Type: application/json" \
    -d '{"command": "echo hello"}' | jq -j .stdout
)
```

Output

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

`destroyed` confirms deletion. `destroying` means cleanup is pending. Check the [operation](https://boxrouter.ai/docs/reference/operation.md) or
[sandbox state](https://boxrouter.ai/docs/reference/get.md) before retrying; [Recover from Errors](https://boxrouter.ai/docs/errors.md#when-cleanup-fails)
covers the cases that remain.

## Read the Response

Responses carry an `x-request-id` header; quote it when you contact support. Lists take `limit`
(1 to 100, default 20) and `cursor`, and return `next_cursor`, which is `null` on the last page.

## Find an Endpoint

The OpenAPI 3.1 document for this API is [openapi.json](https://boxrouter.ai/docs/openapi.json), also as [openapi.yaml](https://boxrouter.ai/docs/openapi.yaml). Import it into a generator, a request client, or an API testing tool.

The sidebar groups the reference by resource. It lists the operations your code can call; the dashboard's own sign-in and account operations are not listed.


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