# Authentication & API keys

> Scoped, revocable bearer keys — mint, rotate, revoke, and the scope model


Every API request authenticates with a bearer key:

```bash
curl -s $BASE_URL/v1/me -H "Authorization: Bearer ds_live_..."
```

**API keys authenticate the `/v1` API only.** The legacy `/api/*` routes are
the web app's private, cookie-authenticated surface — a key used there gets a
typed 403 pointing back here.

Keys are minted at **[Settings → API keys](/settings/api-keys)** (or via
`POST /v1/keys` with an existing admin key). The secret is shown **once**;
DeepSieve stores only a hash.

## Scopes

A key holds `resource:action` scopes; a request needs the route's scope or it
gets `403 insufficient_scope`. Presets:

| Preset | Scopes | Use for |
|---|---|---|
| **Agent** (default) | `runs:*`, `data:read`, `reports:*`, `export:read`, `blueprint:read`, `blueprint:create` (create-only), `blueprint:propose` (ask-only), `webhooks:*` | coding assistants, agents — can start runs (prepaid credits; cap with a per-key budget), manage reports, and create NEW Blueprints. Editing the active Blueprint is human-only regardless of scopes |
| **Read-only** | all `:read` scopes | dashboards, sync jobs |
| **Full** | everything except platform admin | trusted backends |

Custom scope lists: `POST /v1/keys {"name": "...", "scopes": ["runs:read", ...]}`.
A key can never exceed its minter: it stores the minter's role, and a key can't
mint or rotate its way to scopes it doesn't hold.

**Some scopes require another, and we add it for you.** `blueprint:propose`
requires `data:read`: the propose response contains the Blueprint's full spec,
so it asks the same rights as reading that spec directly. Request
`blueprint:propose` on its own and the key is created with `data:read` as well,
rather than failing at its first call. **The `scopes` array in the response is
what was actually granted** — compare it with what you asked for to see any
addition. Nothing unrelated to your request is ever added.

## Rotate and revoke

```bash
curl -s -X POST $BASE_URL/v1/keys/{key_id}/rotate -H "Authorization: Bearer ds_live_..."
```

Rotation returns a new secret; the old one keeps working for **24 hours** so you
can redeploy without downtime. `DELETE /v1/keys/{key_id}` revokes immediately.

## Optional per-key limits

At mint time you can set expiry, a workspace pin, and budgets — in the
create-key dialog (expiry + budgets) or via `POST /v1/keys` (`expires_at`,
`workspace_id`, `budget_runs`, `budget_cents`). A key at its budget gets
`402 budget_exceeded` — the agent should stop and alert a human. Combined with
prepaid billing, the worst an agent can do is exhaust its budget and stop; a
key can never generate an invoice.

## Browser login for MCP (OAuth 2.1)

When you add DeepSieve to a coding agent over `--transport http`, you don't
paste a key — the client runs an OAuth 2.1 browser flow. The consent screen is
**DeepSieve's own login** (via our identity provider); it grants a research +
read session (`runs:*`, `data:read`, `reports:read`), never billing or
key-management scopes — a browser session is a person, not a service
credential. Tokens are short-lived and refresh silently; revoke a client's
access from your account without affecting your API keys. Discovery follows
RFC 9728: the endpoint advertises itself at
`/.well-known/oauth-protected-resource`. Use an **API key** instead for CI,
containers, and server-to-server, where no browser exists.
