# Authentication

> Authenticate every Bookbag API request with a Bearer API key. How to create a key in the dashboard, workspace and agent scoping, and how to keep keys safe.

Every authenticated Bookbag endpoint expects an API key passed as a Bearer token in the `Authorization` header:

```http
Authorization: Bearer oc_live_3f9a...c2
```

Keys look like `oc_<env>_<random>` where `<env>` is `live` or `test`. Only a SHA-256 hash of the key is stored on our side — the plaintext is shown to you exactly once, at creation time.

## Create an API key

1. **Open API keys** — In the dashboard, go to the API keys page (`/apikeys`). You need the **admin** role on the workspace.
2. **Create the key** — Click New key, give it a label, and optionally scope it to a single agent. The full key is displayed once.
3. **Store it securely** — Copy the `oc_...` value into your secret manager immediately. You cannot retrieve it again — if you lose it, delete the key and create a new one.

> **KEYS ARE SHOWN ONCE:** The plaintext key is returned only in the creation response. After that, the dashboard shows just the prefix (e.g. `oc_live_3f9a`) and the last-used time. Treat the key like a password.

## Scoping

Keys carry two scopes that the API enforces on every request:

| Scope | Behavior |
| --- | --- |
| **Workspace** | Every key belongs to one workspace. It can only access agents and data in that workspace. A request that targets an agent in another workspace returns `404`. |
| **Agent (optional)** | When you scope a key to a specific agent at creation time, it can act only on that agent. Targeting any other agent returns `403` (`AUTH_INSUFFICIENT_PERMISSIONS`) on v2, or `404` on the v1 contacts surface. |

> **TIP:** Use an agent-scoped key for a public-facing or single-product integration so a leaked key can't touch the rest of your workspace.

## Managing keys

Keys are managed from the dashboard. The same endpoints back the UI:

| Method | Path | Purpose |
| --- | --- | --- |
| GET | /apikeys | List keys (label, prefix, agent scope, last used). Never returns the secret. |
| POST | /apikeys | Create a key. Body: `{ label?, agentId? }`. Returns the plaintext `key` once. |
| DELETE | /apikeys/:id | Revoke a key immediately. |

These management endpoints use your dashboard session (admin role), not a Bearer key.

## Using the key

```bash
curl https://app.bookbag.ai/api/v2/agents/123/conversations \
  -H "Authorization: Bearer $BOOKBAG_API_KEY"
```

## Authentication errors

| Status | Code | Meaning |
| --- | --- | --- |
| 401 | AUTH_MISSING_API_KEY | No `Authorization: Bearer` header was sent. |
| 401 | AUTH_INVALID_API_KEY | The key is unknown, malformed, or revoked. |
| 403 | AUTH_INSUFFICIENT_PERMISSIONS | The key is agent-scoped and the request targets a different agent. |

The v1 contacts surface returns the same statuses with a Chatbase-style body — `{ "message": "No API key provided." }` or `{ "message": "Invalid API key." }`. See [Errors](/docs/api/errors) for the full envelope.

> **NEVER EXPOSE KEYS IN THE BROWSER:** API keys grant server-side access to your workspace. Call the API from your backend, not from client-side JavaScript. For in-page chat, use the [JavaScript embed](/docs/developers/javascript-embed) instead, which needs no key.
