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

# API keys

Create API keys with only the scopes an agent needs, a spend cap and an expiry; rotate them, revoke them, and check what a key can do.

Give each agent, service or CI job its own key, with only the scopes it needs. Creating keys needs `platform:write`, which the default key does not have: create the first such key in the dashboard.

## Create a key

**curl**

```bash
    curl -s https://beta-api.b3os.org/v1/keys \
      -H "Authorization: Bearer $B3OS_ADMIN_KEY" \
      -H "Content-Type: application/json" \
      -d '{"name": "research agent",
           "scopes": ["vision:run", "sandboxes:write", "sandboxes:run", "infer:run"],
           "spend_cap_usd": 20,
           "expires_at": "2026-12-31T00:00:00Z"}'
    ```
  **TypeScript**

```ts
    import { B3OS } from "@b3os/sdk";

    const admin = new B3OS({ apiKey: process.env.B3OS_ADMIN_KEY });
    const key = await admin.keys.createKey({
      name: "research agent",
      scopes: ["vision:run", "sandboxes:write", "sandboxes:run", "infer:run"],
      spend_cap_usd: 20,
      expires_at: "2026-12-31T00:00:00Z",
    });
    console.log(key.secret); // shown once
    ```
  **Python**

```python
    import os
    from b3os import B3OS

    admin = B3OS(api_key=os.environ["B3OS_ADMIN_KEY"])
    key = admin.keys.create_key({
        "name": "research agent",
        "scopes": ["vision:run", "sandboxes:write", "sandboxes:run", "infer:run"],
        "spend_cap_usd": 20,
        "expires_at": "2026-12-31T00:00:00Z",
    })
    print(key["secret"])  # shown once
    ```
  **MCP**

Create keys from your own code, the dashboard or the CLI's `b3os keys`, not through an agent. Keep `platform:write` out of the keys your agents hold, so an agent can never mint itself a wider key.

The response includes `secret`, the only time it is returned, and `redacted`, the start and end of the secret for recognizing it later. Store the secret in your secret manager.

| Field | Meaning |
|---|---|
| `scopes` | Required. Scopes from the [catalog](/concepts/auth#scopes), `<product>:*` or `*`; never more than the creating key holds |
| `spend_cap_usd` | Requests fail with `spend_cap_exceeded` once the key has spent this much in the billing period; never above the creating key's cap |
| `expires_at` | When the key stops working |
| `project_id` | Another project in the same organization; your key's project by default |

Asking for scopes or a cap beyond your own fails with [`forbidden`](/errors#forbidden), reason `scope_escalation`.

## Check a key

`GET /v1/keys/current` works with any valid credential and returns its kind, organization, project, scopes, spend cap and expiry. `GET /v1/keys` lists the organization's keys (filter by `project_id` or `status`), and `GET /v1/keys/{key_id}` shows one with its spend this period and its last use. Secrets are never returned after creation.

## Change, rotate and revoke

- `PATCH /v1/keys/{key_id}` changes the name, spend cap, expiry or metadata. Scopes never change; create a new key instead.
- To rotate, create a new key with the same scopes, deploy it, then revoke the old one.
- `DELETE /v1/keys/{key_id}` revokes the key at once. Open streams authenticated by it close within a minute, and routines published with it pause their triggers until a new version is published with a valid key. A revoked key stays listed for audit.

## Keys and sandboxes

Code in a sandbox never needs a key: it calls `http://127.0.0.1:7700` and runs with the scopes of the sandbox's grant, a subset of the key that created it. Do not put API keys in sandbox environment variables. See [agents in sandboxes](/sandboxes/agents).