---
title: Authentication
description: Bearer tokens, the API key lifecycle, and how the dashboard and external clients share the same surface.
---

Every endpoint except `/health` requires a bearer token. Two kinds are accepted:

1. **API keys** — long-lived, opaque tokens prefixed `sf_`. Use these for CLIs, server-to-server, and headless MCP clients. Created and revoked on [/settings/api-keys](/settings/api-keys).
2. **Clerk session JWTs** — short-lived, used implicitly by the dashboard. Not for external clients.

The MCP endpoint (`/mcp`) additionally accepts **OAuth access tokens** obtained through the standard MCP sign-in flow — see [MCP](/docs/mcp). Interactive MCP clients should prefer that over API keys; requests authenticated this way are scoped to your default API key.

The header form is identical in both cases:

```
Authorization: Bearer <token>
```

## Lifecycle

| Step | Where | Notes |
|---|---|---|
| Create | `POST /me/keys` (or the dashboard) | Returns the plaintext **once**. Hashed at rest. |
| List | `GET /me/keys` | Returns metadata only — `id`, `name`, `active`, `createdAt`, `tail`. Never the plaintext. |
| Revoke | `DELETE /me/keys/:id` | Sets `active = false`. Existing executions remain queryable; new requests with that key return `401 invalid api key`. |

Lost a key? Revoke it and mint a new one. There is no recovery path.

## Picking which key a request belongs to

When you call the API as a signed-in dashboard user, the request defaults to your **first active key**. To scope a run to a specific key, send the key's id in `X-Use-Api-Key`:

```
Authorization: Bearer <Clerk JWT>
X-Use-Api-Key:  k_…
```

The server verifies the key is yours and active before honouring it. Invalid or foreign ids are ignored silently and the default key is used.

The dashboard sets this header automatically based on the API key dropdown on **Run scrape** — see [Quickstart](/docs/quickstart) for the flow.

When you call the API with an `sf_…` bearer token directly, the request is scoped to that key by definition. `X-Use-Api-Key` is ignored.

## Errors

| Status | Body | Cause |
|---|---|---|
| `401` | `{ "error": "missing or malformed Authorization header" }` | No bearer, or scheme wasn't `Bearer`. |
| `401` | `{ "error": "invalid api key" }` | `sf_…` token doesn't match any active hash. |
| `401` | `{ "error": "invalid token" }` | Clerk couldn't verify the JWT (expired, wrong audience, etc.). |
| `403` | `{ "error": "this endpoint requires a signed-in user" }` | You called a user-only endpoint (e.g. `/me/keys`) with an `sf_…` token. |
