<!-- https://leantools.ai/doc/authentication · HTML twin of this page; the whole documentation: https://leantools.ai/llms-full.txt -->
# Authentication

Getting started

Every call to the MCP server, the tools API and the REST API authenticates with one credential: a workspace API key, sent as a bearer token.

```
Authorization: Bearer lt-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

## API keys

- **Format.** `lt-` followed by 32 URL-safe base64 characters (35 characters in total). Keys issued before the LeanTools rename start with `gsk-` and remain valid.
- **Where they come from.** Every new workspace gets a `default-key` at sign-up. Owners and Admins create more under Settings → API keys; Developers can create keys for themselves. Operators of the host can also issue keys from the admin panel.
- **What they identify.** Exactly one workspace, which is billed for every call made with the key, and the member who created it, who is shown next to each call in the activity log.
- **Storage.** Only the SHA-256 hash is stored. The plaintext is shown once at creation (and at rotation) and cannot be retrieved later; the console shows the prefix, `••••••••` and the last four characters.

### Scope, cap and expiry

A key carries three limits, all set in the console:

| Limit | Effect | Refusal |
| --- | --- | --- |
| Scope | "Everything enabled", or a list of tools the key may reach (`Search`, `Browser`, the seven LeanModel catalogues from `TextAssistant` to `RealtimeAssistant`, `Email`, `Machine`, `Data`, `Connectors`, …). Connectors can additionally restrict which keys may call them. | `403 service_not_allowed`, `403 key_not_allowed` |
| Monthly cap | Credits the key may spend in a calendar month (UTC). The dashboard shows spend against cap per key. | `402 key_cap_reached` |
| Expiry | A date after which the key is refused. Rotate it to issue a fresh key with the same settings and revoke the old one at once. | `401 key_expired` |

## Admission order

Each call is checked in this order: credential → workspace status → tool enabled → key scope → tool budget and key cap → credits → tool-specific guardrails. Credentials and workspace policy are cached for 60 seconds (`LeanTools.Credits.CredentialCacheSeconds`); changes made in the console invalidate the cache for that workspace at once, so a revoked key or a disabled tool is refused immediately.

## Where the key goes

- **Header** for everything: `Authorization: Bearer <key>`.
- **Query string** only where a header is impossible: the LeanBrowser WebSocket upgrades (`?token=…`), the LeanVision live feed and the LeanModel realtime audio socket. Every other endpoint rejects a key in the query so keys never end up in access logs or browser history.
- **WebSocket subprotocol** on the realtime audio socket only, for browser clients that can set neither headers nor a safe query string: offer `openai-insecure-api-key.<key>` as a subprotocol, as OpenAI's browser clients do; LeanTools accepts it and echoes `realtime` as the agreed subprotocol when it was offered too (see the [reference](https://leantools.ai/doc/api#realtime)).
- **LeanMemory's high-throughput columnar data channel** (gRPC; see the [reference](https://leantools.ai/doc/api#leanmemory)) takes the same bearer in the `authorization` gRPC metadata.

## Error envelope

A refused call answers a JSON envelope with a stable `errorCode`:

```
{ "isSuccess": false, "errorMessage": "This API key has expired. Create a new key in the console.", "errorCode": "key_expired" }
```

The OpenAI-compatible endpoints use the OpenAI envelope (`{ "error": { "message", "type", "code" } }`); the MCP server answers JSON-RPC errors for protocol failures and `isError: true` results for tool failures. The full list is on the [Errors](https://leantools.ai/doc/errors) page.

## Console sign-in

The workspace console at [/app](https://leantools.ai/app) and the public site use a separate, per-person sign-in (email and password) that issues a short-lived session token in a cookie. API keys never sign in to the console, and console sessions never authenticate API calls. Invite teammates from Settings → Members; each accepts with their own account.

> Treat API keys as secrets. Put them in environment variables or your harness's secret store, never in prompts, repositories or client-side code. If a key leaks, revoke it in the console: every agent using it is refused within a minute.
