# API keys

> Create keys for services and CI, limit them to the actions, sandboxes and environments they need, and rotate or revoke them.

API keys authenticate machines: your backend, CI jobs, scripts and SDKs. People use the dashboard and the CLI's browser sign-in instead.

```text
Authorization: Bearer sbx_live_3QkVt9…
```

A key is `sbx_live_` followed by 40 characters. We store only a hash: the full secret is shown **once**, when you create or rotate it.

## Create a key

On the dashboard: **API keys → Create key**. Choose a name, an expiry and a scope, then copy the secret.

```bash title="CLI"
sbx api-key create ci-runner --ttl 90d --preset ci
sbx api-key create metrics --preset read-only --ttl 365d
sbx api-key create one-sandbox --sandbox sb_k3v9x2mq --actions sandbox.read,exec,file.read --ttl 7d
```

```bash title="curl"
curl -sS -X POST https://sandboxer.com/api/v1/api-keys/scoped \
  -H "Authorization: Bearer $SANDBOXER_ADMIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"ci-runner","ttl":"90d","preset":"ci"}'
```

Creating keys needs a browser session or a key with the `admin` preset.

## Scopes

A request is allowed only when **both** hold:

1. its action is in the key's actions, and
2. its target sandbox is in the key's sandboxes — or in one of its environments.

Everything else is denied.

| Action | Allows |
| - | - |
| `sandbox.create` | Create sandboxes |
| `sandbox.read` | List and inspect |
| `sandbox.update` | Rename, extend, recover |
| `sandbox.stop` / `sandbox.resume` / `sandbox.fork` | Those lifecycle calls |
| `sandbox.delete` | Delete any sandbox in scope |
| `sandbox.delete-own` | Delete only sandboxes this key created |
| `exec` | Run commands |
| `file.read` / `file.write` | Read / write files |
| `ssh` | SSH, scp, port forwarding |
| `host` | Host ports |
| `desktop` | Desktop and browser streams |
| `agent.prompt` | Prompt, steer, interrupt |
| `snapshot.read` / `snapshot.write` | Snapshots and named snapshots |
| `environment.read` / `environment.write` | Environments and secrets |
| `account.read` | `/me`, `/limits`, list keys and organizations |
| `account.admin` | Webhooks, telemetry, keys, organizations |
| `*` | Everything |

### Presets

| Preset | For | Includes |
| - | - | - |
| `read-only` | Dashboards, monitoring | Read sandboxes, files, snapshots, environments |
| `full-sandbox` | Operating existing sandboxes | Everything on a sandbox except create / resume / fork / delete and account writes |
| `ci` | Pipelines | `full-sandbox` plus create, resume, fork and delete-own |
| `admin` | Automation that manages the account | `*` |

A key keeps the actions it was created with, even if a preset gains actions later.

### The in-sandbox key

Every sandbox gets `SANDBOXER_TOKEN`: a key limited to **that sandbox**. It can prompt, exec, read and write files, host ports, use SSH and the desktop, and read its own snapshots — but not save, share or delete snapshots, create, resume, fork or delete sandboxes, edit environments or touch the account. If a sandbox is compromised, the blast radius is that sandbox.

A `noEnv` sandbox, or one on an environment marked safe for third parties, gets a narrower token: it can only read that sandbox and host its ports (`sandbox.read`, `host`). It can't see any other sandbox or your account.

## List and inspect

```bash
sbx api-key list           # prefix, last four, scope, expiry, 30-day requests, live resources
sbx api-key usage key_123 --verbose
```

`GET /api-keys` returns the same, plus a catalog of actions and presets. `GET /api-keys/{id}/usage` lists the sandboxes the key created. Usage stays readable after you revoke a key.

## Rotate and revoke

```bash
sbx api-key rotate key_123     # new secret, shown once; the old one stops working now
sbx api-key revoke key_123     # the key stops working; its sandboxes keep running
```

Rotate on a schedule and whenever a secret may have leaked. Keys expiring within 14 days are flagged `expiringSoon` and highlighted on the dashboard.

## Storing keys

- Put keys in a secret manager or your CI's secret store, never in code or images.
- Use `--key-stdin` for the CLI so the secret stays out of process arguments.
- Give each service its own key, with the narrowest preset and the shortest TTL that works.

## Errors

| `code` | Meaning |
| - | - |
| `unauthorized` (401) | Missing, malformed, revoked or unknown key |
| `api_key_expired` (403) | The key's TTL elapsed |
| `api_key_action_forbidden` (403) | The action is not in the key's scope |
| `api_key_sandbox_forbidden` (403) | The sandbox or environment is not in the key's scope |
