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

# Keys and sandbox

> Mint, list and revoke credentials, including disposable sandbox keys.

## What this family does

Keys are organization credentials. Minting returns the plaintext once and stores only its SHA-256 hash, which is also the `id` you list and revoke by. A key can never be minted with a scope its creator does not already hold.

The sandbox routes mint `exo_sandbox_` keys bound to an isolated partition, so you can exercise the API without touching real data, and wipe that partition when you are done.

## Routes

| Method   | Path                                                                  | Scope   | What it does                       |
| -------- | --------------------------------------------------------------------- | ------- | ---------------------------------- |
| `GET`    | [`/v1/keys`](/api-reference/keys/list-keys)                           | any key | List API keys                      |
| `POST`   | [`/v1/keys`](/api-reference/keys/create-key)                          | any key | Mint an API key                    |
| `DELETE` | [`/v1/keys/{key_id}`](/api-reference/keys/delete-key)                 | any key | Revoke an API key                  |
| `GET`    | [`/v1/sandbox/keys`](/api-reference/keys/list-sandbox-keys)           | `admin` | List sandbox keys                  |
| `POST`   | [`/v1/sandbox/keys`](/api-reference/keys/create-sandbox-key)          | `admin` | Mint a sandbox (test-mode) API key |
| `DELETE` | [`/v1/sandbox/keys/{key_id}`](/api-reference/keys/delete-sandbox-key) | `admin` | Revoke a sandbox key               |
| `POST`   | [`/v1/sandbox/reset`](/api-reference/keys/reset-sandbox)              | `admin` | Wipe the sandbox partition         |

## Headers that apply here

Neither header is declared as an OpenAPI parameter, so neither appears in the generated schema tables.

* **`Idempotency-Key`** makes a retry safe on `POST /v1/keys`, `POST /v1/sandbox/reset`. See [Idempotency](/platform/idempotency).

## The shape of a call

Mint a key.

<CodeGroup>
  ```bash cURL theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
  curl -X POST https://api.get-exo.com/v1/keys \
    -H "X-Exo-API-Key: $EXO_KEY" \
    -H "Content-Type: application/json" \
    -d '{"label": "ci-pipeline", "scopes": ["read"]}'
  ```

  ```python Python theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
  import httpx

  response = httpx.post(
      "https://api.get-exo.com/v1/keys",
      headers={"X-Exo-API-Key": EXO_KEY},
      json={"label": "ci-pipeline", "scopes": ["read"]},
  )
  plaintext = response.json()["apiKey"]  # shown once
  ```

  ```typescript TypeScript theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
  const response = await fetch("https://api.get-exo.com/v1/keys", {
    method: "POST",
    headers: {
      "X-Exo-API-Key": process.env.EXO_KEY!,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ label: "ci-pipeline", scopes: ["read"] }),
  });
  const { apiKey } = await response.json(); // shown once
  ```
</CodeGroup>

The response:

```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
{
  "id": "6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b",
  "apiKey": "exo_prod_9f2c41a8b7e04d3c8a1f6e2b5d90c743",
  "label": "ci-pipeline"
}
```

## Traps

**The plaintext is unrecoverable.** There is no route that returns it again. Store it before you close the response.

**A key that is not yours is 404, not 403.** That keeps key hashes unenumerable. Revoking your own already-revoked key is a 204, so retries are safe.

**Minting a sandbox key does not replay.** A retried `Idempotency-Key` mints a fresh key, because persisting a live credential in the replay store would undo the hash-only-at-rest design.

**Sandbox keys are contained.** Their scopes are fixed at `read` and `write`, and they cannot select another subject.

## Related

<Columns cols={2}>
  <Card title="API reference" icon="book-open" href="/api-reference/overview">
    Base URL, authentication, and the conventions every route shares.
  </Card>

  <Card title="Errors" icon="circle-alert" href="/platform/errors">
    The problem envelope and the full code catalogue.
  </Card>
</Columns>
