# API authentication

> Send an API key as a bearer token. How keys look, what scopes they have, how to rotate one, and what is not allowed.

Page: https://scribiz.com/docs/api/authentication

Every call that is not anonymous carries an API key.

```bash
curl https://scribiz.com/api/v1/me \
  -H "Authorization: Bearer $SCRIBIZ_API_KEY"
```

You can send the key in an `X-API-Key` header instead. Use one or the other. A key in a URL, such as `?key=...`, is ignored, and the call is then anonymous.

## What a key looks like

`sbz_live_` followed by random characters. The prefix lets secret scanners, such as GitHub's and gitleaks, find a key that was committed by mistake.

Keys are created in the dashboard, under API keys, after you sign in with an email link. Copy the key when it appears. It is shown once. Scribiz keeps only a hash of it, so a lost key cannot be shown again.

Creating and revoking a key needs a signed-in session, which the dashboard has. A key cannot do either, even one with the `all` scope. `POST /v1/keys` and `DELETE /v1/keys/:id` with a key answer `403` with `session_required`. `GET /v1/keys` works with an `all` key.

```json title="GET /v1/keys"
{
  "keys": [
    {
      "id": "key_GDAeFMIBQ2lh",
      "name": "CI",
      "prefix": "sbz_live_",
      "last4": "rz97",
      "scopes": "all",
      "createdVia": "dashboard",
      "deviceLabel": null,
      "createdAt": 1791093414824,
      "lastUsedAt": 1791093428666,
      "expiresAt": null
    }
  ]
}
```

A key made through command-line sign-in (`scribiz login`, from version 0.1.1 of the CLI) has `createdVia: "cli"` and the name of the machine in `deviceLabel`, and is named `CLI on` and that machine. It has the scope `all`. Revoke it like any other key.

## Scopes

| Scope | Allows |
| --- | --- |
| `all` | Everything your account can do through a key |
| `mcp` | Calling the remote MCP server at `/mcp`, and everything `read` allows |
| `read` | Reading jobs and your account. It cannot create or delete jobs. |

Give a key the smallest scope that works. A key for an agent needs only `mcp`. A dashboard that shows results needs only `read`. Creating or deleting a job needs `all`. A `read` key that tries answers `403` with `insufficient_scope`.

An account can have up to 10 active keys. Another one is `409` with `key_limit_reached`. The dashboard lists when each key was last used.

## Rotate a key

1. Create a new key.
2. Deploy it to the place that uses the old one.
3. Check that the old key's last-used time stops moving.
4. Revoke the old key.

A revoked key stops working at once on the server that revoked it, and within about a minute everywhere.

## Rules

- **Keep keys on a server.** Calls with an API key are not meant to come from a browser. The API answers cross-origin browser requests only for Scribiz's own site, so a page on another origin cannot use a key. This is deliberate: a key in a web page is not a secret. Call your own server from the page, and call Scribiz from there.
- **Do not put keys in URLs.** Keys are only accepted in headers.
- **One key per service.** Then you can revoke one without breaking the rest.
- **One balance.** A key draws on the minutes of the account that made it. MCP and the API share that balance.

## Anonymous requests

The web tool works without a key for captions and a small daily quota of Listen and Watch. Those calls come from a browser. When the server has Cloudflare Turnstile on, a Listen or Watch request needs a `turnstile_token`. Do not build on anonymous access. It is limited by connection, it is rate limited, and Listen and Watch can be switched off when demand is high. Use a key.

## Browser sessions

The Scribiz website uses a cookie session for the signed-in pages. A session is not an API credential. Code uses an API key. Signing in from the command line (`scribiz login`) ends with a key too: the CLI swaps the browser approval for an API key and the session ends. See [Sign in from the CLI](https://scribiz.com/docs/authentication.md#sign-in-from-the-cli).

## Errors

| Status | Code | Meaning |
| --- | --- | --- |
| 401 | `unauthorized` | No credential, on a route that needs one |
| 401 | `invalid_api_key` | The key does not exist, was revoked or expired |
| 403 | `insufficient_scope` | The key's scope does not allow this call |
| 403 | `session_required` | The call needs a signed-in session, not a key |
| 402 | `quota_exceeded` | The account has no minutes left |

```json title="401 Unauthorized"
{ "error": { "code": "invalid_api_key", "message": "This API key is not valid. It may have been revoked or expired." } }
```

See [Errors and limits](https://scribiz.com/docs/api/errors.md).

---

Checked against the Scribiz build on 2026-10-05.
