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

# Authentication

> Every /v1 route takes an API key in the Authorization header:

## Bearer API keys

Every `/v1` route takes an API key in the `Authorization` header:

```
Authorization: Bearer ph_live_...
```

Keys are issued in the dashboard under API keys. A key is shown once at creation and stored hashed; when you create one you can give it a name, a spend limit in credits, an expiry (a number of days, or `7d`, `30d`, `90d`, `1y`) and a spend reset period (`daily`, `weekly`, `monthly`, `never`). Revoke a leaked key from the same page. Keep keys server side: a key is full control of your browser sessions and your credits.

A request without the header answers 401:

```json theme={"dark"}
{"detail": "Missing API key (Authorization: Bearer ph_...)"}
```

A request with a bad, revoked or expired key answers 401 as well. Both carry `WWW-Authenticate: Bearer`:

```json theme={"dark"}
{"detail": "Invalid API key"}
```

Two other answers come from the key itself rather than the request:

* 402 with a plain `detail` string when the key has spent its own credit limit. Raise the limit in the dashboard or use another key.
* 503 `{"detail": "auth backend unavailable, please retry"}` with `Retry-After: 2` when the key could not be checked. Retry; do not rotate the key.

## Scopes

Each key carries scopes, and every route checks the scope its method needs:

| Scope | What it allows |
| - | - |
| `sessions:read` | GET, HEAD and OPTIONS routes: reading sessions, tabs, usage, credits, lists. |
| `sessions:write` | Every other method: creating sessions, every page read and drive call, every billable route. Includes `sessions:read`. Two GET routes also need it because they hand over control: `GET /v1/session/{id}/connect` (a raw CDP URL) and the sidecar HTTP passthrough. |
| `cdp` | Full control of a session, read and write. This is the scope the MCP OAuth flow issues; it also satisfies `/connect` and the MCP server. |
| `admin` | Everything. |

Dashboard-issued keys carry `sessions:read` and `sessions:write`. A key that lacks the scope a route needs answers 403 with a plain string, `this API key lacks the 'sessions:write' scope`. The hosted MCP server needs `sessions:write` or `cdp` and answers 403 with `{"error": "..."}`.

## Request ids

Every response carries an `X-Request-ID` header with a 16-character id, including 401 and 404 answers, 429 refusals, 500s, and the empty 204 and 405 answers. Quote it when you contact support. Inbound `X-Request-ID` values are not echoed; the API assigns its own.

## Idempotency

`POST /v1/session` accepts an `Idempotency-Key` header. The key is scoped to your account:

* A second create with the same key answers 201 with the same session, as long as that session is alive: same `session_id`, same `created_at`, a fresh `connect_url`.
* If the first create is still running, the second waits for it, for up to about 185 seconds, and then returns the same session.
* The key is remembered for an hour after the create finishes. If the session has ended by the time you retry, the key is released and the retry creates a new session.
* 409 `{"detail": {"error": "idempotency_key_in_use", "message": "..."}}` when a create under this key could not be settled in time. Retry shortly.

Use it whenever you retry a create after a timeout, so a retry can never leave you paying for two browsers.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.