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

# Box Public API v1

> Create, manage, prompt, observe, and expose Box sandboxes from your own products.

Use the Box Public API from services, CI jobs, hosted workers, and product automation code. For typed clients, start with the [Box SDKs](/box/sdks/overview), [Python SDK](/box/sdks/python), or [TypeScript/JavaScript SDK](/box/sdks/typescript).

## Recommended flow

1. Create a Box API key with `box api-key create` or in the dashboard.
2. Create a box with `POST /boxes`, or resume/fork an existing box when the user is continuing prior work.
3. Poll `GET /boxes/{boxId}` until the box is `ready` or `idle`.
4. Queue work with `POST /boxes/{boxId}/prompt`.
5. Read `GET /boxes/{boxId}/events` while work is running.
6. Use `POST /boxes/{boxId}/desktop` when the user or your support team needs live computer-use visibility.
7. Stop/archive, resume, fork, or delete the box according to your retention policy.

Everything the Box CLI does programmatically is available here: first-class endpoints cover lifecycle, prompts, events, files, desktop, and snapshots. Anything else, including in-box tools like `host` and `lux`, package installs, or your own scripts, is driven through [`POST /boxes/{boxId}/commands`](/box/api/reference/agent/execute-box-command). For high-frequency or streaming control, run your own daemon in the box; see [the daemon pattern](/box/platform-guide#bring-your-own-harness-the-daemon-pattern).

## Base URL

```text theme={null}
https://ascii.dev/api/box/v1
```

## Authentication

Pass a Box API key as a bearer token on API requests:

```bash theme={null}
curl -sS "https://ascii.dev/api/box/v1/boxes" \
  -H "Authorization: Bearer $BOX_API_KEY"
```

Create, rotate, and revoke service keys with the `box api-key` CLI command or in the Box dashboard. For key lifecycle guidance, see [API Keys](/box/api-keys).

<Warning>
  Treat Box API keys and returned desktop URLs as secrets. Desktop and VNC URLs can contain access tokens and should not be logged or persisted unredacted.
</Warning>

Updating `PATCH /account/data-retention` is the exception: it requires an interactive Box session and refuses API keys. Per-Box keys are restricted to their Box; use an account service key for account-wide reads.

## Response envelope

Every v1 JSON response has an explicit success discriminator:

```json theme={null}
{
  "ok": true,
  "type": "box.list",
  "boxes": []
}
```

| Field  | Description                                                                                                    |
| ------ | -------------------------------------------------------------------------------------------------------------- |
| `ok`   | `true` for success, `false` for failure.                                                                       |
| `type` | Stable response or event discriminator, such as `box.list`, `box.created`, `box.stopping`, or `prompt.queued`. |

## Error model

HTTP status remains authoritative. Error bodies also use a structured JSON envelope:

```json theme={null}
{
  "ok": false,
  "type": "box.error",
  "status": 409,
  "code": "provider_not_configured",
  "message": "Prompting is locked until Codex is configured on the Agents page.",
  "error": {
    "code": "provider_not_configured",
    "message": "Prompting is locked until Codex is configured on the Agents page.",
    "status": 409,
    "details": {
      "provider": "codex",
      "setupUrl": "https://box.ascii.dev/dashboard?tab=agents"
    }
  },
  "requestId": "req_..."
}
```

| Status | Typical codes                                                                                                                           | What to do                                                                                                |
| ------ | --------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `400`  | `invalid_json`, `prompt_required`, `provider_required`, `invalid_name`, `no_changes`, `machine_not_running`, `trial_auto_stop_required` | Fix the request body, wait for the box machine to start, or retry once the box reaches a runnable state.  |
| `401`  | `unauthorized`                                                                                                                          | Provide a valid bearer token.                                                                             |
| `402`  | `billing_required`, `team_member_cap_reached`                                                                                           | Send the user to the returned billing URL, or ask the organization owner to raise the member's usage cap. |
| `403`  | `trial_machine_class_not_allowed`, `org_suspended`                                                                                      | The requested machine size needs a payment method, or the organization wallet is suspended.               |
| `404`  | `not_found`                                                                                                                             | Refresh local state; the box or key no longer exists.                                                     |
| `409`  | `account_not_ready`, `provider_not_configured`, `box_not_promptable`, `resume_failed`, `fork_failed`                                    | Resolve the prerequisite or resource-state conflict.                                                      |
| `429`  | `rate_limited`, `daily_limit_reached`, `limit_reached`, `member_limit_reached`                                                          | Back off and show the limit message.                                                                      |
| `5xx`  | `invalid_json_response`, `stream_failed`, `http_500`                                                                                    | Retry idempotent calls with jitter and include `requestId` in support logs.                               |

## Idempotent box creation

`POST /boxes` and `POST /boxes/{boxId}/fork` each provision a new box that bills on success, so a lost response (timeout or `5xx` after the box was already accepted) leaves you unsure whether to retry. Send an `Idempotency-Key` header with your own account-unique value (a UUID) to make create and fork safe to retry: the first request creates the box and binds it to the key; every retry with the **same account, key, and request body** returns that same box instead of a second, billable one.

```bash theme={null}
curl -sS -X POST "$BOX_API_BASE/boxes" \
  -H "Authorization: Bearer $BOX_API_KEY" \
  -H "Idempotency-Key: 6f9619ff-8b86-d011-b42d-00cf4fc964ff" \
  -H "Content-Type: application/json" \
  -d '{"from":"web-stack","noEnv":true,"ttlSeconds":1800}'
```

| Situation                                        | Result                                                              |
| ------------------------------------------------ | ------------------------------------------------------------------- |
| Retry after a lost `202` or a `5xx`              | Same key returns the original box (no second box).                  |
| Retry while the first box is still being created | `409` `idempotency_in_progress`. Retry shortly with the same key.   |
| Same key, different request body                 | `409` `idempotency_key_reused`.                                     |
| Create that failed before the box existed        | Key auto-releases within \~2 minutes so a retry can create the box. |

Keys are retained for 24 hours, and are scoped to your account (two accounts can use the same key value with no collision). Omit the header to keep the default (non-idempotent) behavior. The header is optional on every SDK's create and fork call (`Idempotency-Key` / `idempotencyKey`); see [Create box](/box/api/reference/boxes/create-box) and [Fork box](/box/api/reference/boxes/fork-box).

## Box lifecycle

A Box moves through these states:

| State                                    | Meaning                                              | Client behavior                                                             |
| ---------------------------------------- | ---------------------------------------------------- | --------------------------------------------------------------------------- |
| `provisioning`, `provisioned`, `cloning` | The sandbox is being created or prepared.            | Poll `GET /boxes/{boxId}`.                                                  |
| `ready`, `idle`                          | The box can accept prompts and interactive access.   | Prompt, open desktop, SSH, or inspect events.                               |
| `running`                                | The Box is actively working.                         | Read events; interrupt only when the caller intends to stop current work.   |
| `archiving`                              | Stop/snapshot is in progress.                        | Poll until `archived` or another terminal state.                            |
| `archived`                               | The machine is stopped; a snapshot may be available. | Resume or fork.                                                             |
| `error`                                  | Provisioning or runtime failed.                      | Show the error and let the user stop, delete, resume, or fork when allowed. |

<Note>
  `idle` and `running` only reflect work queued through `POST /boxes/{boxId}/prompt`. Processes you run yourself, over SSH or the command endpoint, do not change the state: a box can show `idle` while your own agent works inside it.
</Note>

## Providers, models, and reasoning

`POST /boxes/{boxId}/prompt` accepts the same providers Box uses in the dashboard. The API accepts `codex` and `claude-code`; `claude` is accepted as an alias for `claude-code` by the current backend, but `claude-code` is the canonical value to store in integrations.

Model ids come from the same catalog used by the dashboard model selector. If you omit `model`, Box uses the user's saved dashboard default for that provider. Prefer the listed model ids for predictable behavior.

| Provider      | Models                                                                                                | Reasoning effort values                                                                                                                                                                            | Default         |
| ------------- | ----------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- |
| `codex`       | `gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna`, `gpt-5.5`, `gpt-5.4`, `gpt-5.4-mini`, `gpt-5.3-codex` | GPT-5.6 models: `none`, `low`, `medium`, `high`, `xhigh`, `max`; `gpt-5.5`, `gpt-5.4`, `gpt-5.4-mini`: `none`, `low`, `medium`, `high`, `xhigh`; `gpt-5.3-codex`: `low`, `medium`, `high`, `xhigh` | `gpt-5.6-terra` |
| `claude-code` | `claude-fable-5`, `opus-5`, `opus`, `sonnet`, `haiku`                                                 | `claude-fable-5`, `opus-5`, `opus`: `low`, `medium`, `high`, `max`; `sonnet`: `low`, `medium`, `high`; `haiku`: no reasoning control                                                               | `sonnet`        |

Use the dashboard's provider setup page to configure credentials before prompting. If credentials are missing, the API returns `provider_not_configured` with a setup URL.

## Request examples

These are raw HTTP. Every one of them has a CLI, TypeScript and Python equivalent on the feature page it belongs to: [Snapshots](/box/snapshots), [Environments](/box/environments), [Webhooks](/box/webhooks), [Data retention](/box/data-retention), [Desktop](/box/desktop-streaming), [Hosting](/box/hosting). The [CLI reference](/box/cli-reference) maps commands to endpoints.

Set shared variables:

```bash theme={null}
export BOX_API_BASE="https://ascii.dev/api/box/v1"
export BOX_API_KEY="box_your_secret_here"
```

Create a one-hour box and store its id:

```bash theme={null}
BOX_ID=$(curl -sS -X POST "$BOX_API_BASE/boxes" \
  -H "Authorization: Bearer $BOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"ttlSeconds":3600}' | jq -r '.box.id')
```

Create a box without automatic archival:

```bash theme={null}
curl -sS -X POST "$BOX_API_BASE/boxes" \
  -H "Authorization: Bearer $BOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"ttlSeconds":null}'
```

Poll readiness:

```bash theme={null}
curl -sS "$BOX_API_BASE/boxes/$BOX_ID" \
  -H "Authorization: Bearer $BOX_API_KEY"
```

Prompt a Box to work in a repo:

```bash theme={null}
curl -sS -X POST "$BOX_API_BASE/boxes/$BOX_ID/prompt" \
  -H "Authorization: Bearer $BOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "codex",
    "model": "gpt-5.4",
    "reasoningEffort": "medium",
    "prompt": "Work on the selected repo: run tests, fix failures, commit the result, and report the preview URL if you start a server."
  }'
```

Read Box work and lifecycle events:

```bash theme={null}
curl -sS "$BOX_API_BASE/boxes/$BOX_ID/events" \
  -H "Authorization: Bearer $BOX_API_KEY"
```

Get a desktop streaming URL for live inspection:

```bash theme={null}
curl -sS -X POST "$BOX_API_BASE/boxes/$BOX_ID/desktop?vnc=1" \
  -H "Authorization: Bearer $BOX_API_KEY"
```

To return a VNC URL that does not require an access token, send:

```bash theme={null}
curl -sS -X POST "$BOX_API_BASE/boxes/$BOX_ID/desktop?vnc=1" \
  -H "Authorization: Bearer $BOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"publicAccess":true}'
```

Stop/archive a box when the workflow is complete:

```bash theme={null}
curl -sS -X POST "$BOX_API_BASE/boxes/$BOX_ID/stop" \
  -H "Authorization: Bearer $BOX_API_KEY"
```

Archive keeps snapshots for resume. Permanent delete requires the exact target id as a confirmation header, returns `202`, and cannot be canceled:

```bash theme={null}
curl -sS -X DELETE "$BOX_API_BASE/boxes/$BOX_ID" \
  -H "Authorization: Bearer $BOX_API_KEY" \
  -H "X-Ascii-Confirm-Delete: $BOX_ID"
```

Poll `GET /deletion-operations/{operationId}` until `status` is `completed`. See [Data retention and deletion](/box/data-retention).

## Endpoint reference

| Endpoint                                 | Reference                                                                                                         |
| ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `GET /me`                                | [Get current Box user](/box/api/reference/account/get-current-box-user)                                           |
| `GET /limits`                            | [Get Box limits](/box/api/reference/account/get-box-limits)                                                       |
| `GET /account/data-retention`            | [Get account data-retention policy](/box/api/reference/account/get-account-data-retention-policy)                 |
| `PATCH /account/data-retention`          | [Update account data-retention policy](/box/api/reference/account/update-account-data-retention-policy)           |
| `GET /deletion-operations/{operationId}` | [Get deletion operation](/box/api/reference/account/get-deletion-operation)                                       |
| `GET /repos`                             | [List GitHub repositories available to Box](/box/api/reference/account/list-github-repositories-available-to-box) |
| `POST /repos`                            | [Select repository for Boxes](/box/api/reference/account/select-repository-for-boxes)                             |
| `GET /api-keys`                          | [List API keys](/box/api/reference/account/list-api-keys)                                                         |
| `GET /api-keys/{apiKeyId}/usage`         | [Get API key usage](/box/api/reference/account/get-api-key-usage)                                                 |
| `GET /secrets`                           | [Get Box secrets setup](/box/api/reference/account/get-box-secrets-setup)                                         |
| `POST /secrets`                          | [Update Box secrets setup](/box/api/reference/account/update-box-secrets-setup)                                   |
| `GET /boxes`                             | [List boxes](/box/api/reference/boxes/list-boxes)                                                                 |
| `POST /boxes`                            | [Create box](/box/api/reference/boxes/create-box)                                                                 |
| `GET /boxes/{boxId}`                     | [Get box](/box/api/reference/boxes/get-box)                                                                       |
| `PATCH /boxes/{boxId}`                   | [Update box](/box/api/reference/boxes/update-box)                                                                 |
| `DELETE /boxes/{boxId}`                  | [Permanently delete Box data](/box/api/reference/boxes/permanently-delete-box-data)                               |
| `POST /boxes/{boxId}/stop`               | [Stop and archive box](/box/api/reference/boxes/stop-and-archive-box)                                             |
| `POST /boxes/{boxId}/resume`             | [Resume box](/box/api/reference/boxes/resume-box)                                                                 |
| `POST /boxes/{boxId}/fork`               | [Fork box](/box/api/reference/boxes/fork-box)                                                                     |
| `POST /boxes/{boxId}/prompt`             | [Prompt Box](/box/api/reference/agent/prompt-box-agent)                                                           |
| `GET /boxes/{boxId}/events`              | [List box events](/box/api/reference/agent/list-box-events)                                                       |
| `GET /boxes/{boxId}/prompts/{promptId}`  | [Get prompt run status](/box/api/reference/agent/get-prompt-run-status)                                           |
| `GET /boxes/{boxId}/files`               | [Read a file from a Box](/box/api/reference/agent/read-box-file)                                                  |
| `PUT /boxes/{boxId}/files`               | [Write a file in a Box](/box/api/reference/agent/write-box-file)                                                  |
| `POST /boxes/{boxId}/commands`           | [Execute a command in a Box](/box/api/reference/agent/execute-box-command)                                        |
| `GET /boxes/{boxId}/artifacts`           | [Download a Box artifact](/box/api/reference/agent/download-box-artifact)                                         |
| `POST /boxes/{boxId}/interrupt`          | [Interrupt running work](/box/api/reference/agent/interrupt-running-agent-work)                                   |
| `POST /boxes/{boxId}/desktop`            | [Get desktop streaming URL](/box/api/reference/agent/get-desktop-streaming-url)                                   |
| `POST /boxes/{boxId}/sshkey`             | [Configure box SSH key](/box/api/reference/agent/configure-box-ssh-key)                                           |
| `GET /snapshots`                         | [List snapshots](/box/api/reference/snapshots/list-snapshots)                                                     |
| `GET /boxes/{boxId}/snapshots`           | [List box snapshots](/box/api/reference/snapshots/list-box-snapshots)                                             |
| `GET /boxes/{boxId}/snapshots/latest`    | [Get latest box snapshot](/box/api/reference/snapshots/get-latest-box-snapshot)                                   |
| `GET /snapshots/{snapshotId}/tree`       | [Get snapshot file tree](/box/api/reference/snapshots/get-snapshot-tree)                                          |
| `GET /snapshots/{snapshotId}/files`      | [Download a file or folder from a snapshot](/box/api/reference/snapshots/get-snapshot-file)                       |
| `GET /snapshots/{snapshotId}/download`   | [Get snapshot download](/box/api/reference/snapshots/get-snapshot-download)                                       |
| `DELETE /snapshots/{snapshotId}`         | [Permanently delete snapshot data](/box/api/reference/snapshots/permanently-delete-snapshot-data)                 |
