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

# Authentication

> How API keys and session tokens authenticate Custral requests.

Custral has two ways to authenticate, for two different callers:

1. **API keys**, for server-to-server integrations against the public [`/v1` API](/dev/api-reference/overview). Scoped, org-bound, and what the [SDK](/dev/sdks/typescript) and [MCP server](/dev/mcp/overview) use.
2. **Session tokens**, for the web app and the [CLI](/dev/cli/overview), acting **as a user** across their organizations.

<Tip>
  Connecting an **MCP client** (Claude Code, Claude Desktop, Cursor)? You do not need to create a key at all, point the
  client at the endpoint and it will open your browser to sign in, then receive a scoped key without ever showing you
  one. See [MCP Server](/dev/mcp/overview#sign-in-through-your-browser-recommended).
</Tip>

## API keys (recommended for integrations)

Create a key in **Settings → Applications**. Keys come in two shapes:

| Prefix | Where it runs        | Used by                                            |
| ------ | -------------------- | -------------------------------------------------- |
| `sk_…` | **Server-side only** | `@custral/sdk`, the `/v1` REST API, the MCP server |
| `pk_…` | Browser-safe         | `@custral/js` (usage), `@custral/widget` (chat)    |

<Warning>
  A secret key (`sk_…`) can read and write your workspace data. Keep it on a server, never ship it to a browser or
  commit it. If a key leaks, revoke it in **Settings → Applications**.
</Warning>

Send a secret key as a Bearer token (the `X-Api-Key` header is also accepted and takes precedence):

```bash theme={null}
curl https://api.custral.com/v1/me \
  -H "Authorization: Bearer sk_live_..."
```

A key is bound to one **organization**, so `/v1` requests do **not** need an `Organization` header, the key already identifies the workspace.

### Scopes

Each key holds a set of scopes; every endpoint requires the matching one (fail-closed). Grant only what an integration needs:

| Scope                 | Grants                                            |
| --------------------- | ------------------------------------------------- |
| `records:read`        | Read records                                      |
| `records:write`       | Create + update records                           |
| `objects:read`        | Read the object schema                            |
| `documents:read`      | Read record notes / documents as markdown         |
| `conversations:read`  | Read conversations + dispositions                 |
| `conversations:write` | Update, create, and add messages to conversations |
| `tasks:read`          | Read tasks                                        |
| `webhooks:manage`     | Register / manage webhooks                        |
| `mcp:read`            | Connect an MCP client and read                    |
| `mcp:write`           | Perform MCP write actions                         |

Call [`GET /v1/me`](/dev/api-reference/overview) at any time to confirm a key's org, environment (`live` / `test`), and scopes.

### Keys issued by browser sign-in

An MCP client that signs in through the browser is handed one of these same keys. There is no second kind of token. You choose the workspace on the approval screen, the scopes it asks for are listed there before you authorize, and the result shows up in **Settings → Applications** where you can see its last use and revoke it like any other key.

That means everything on this page applies to it too: the same scope checks, the same `insufficient_scope` errors, the same revocation. The only difference is that nobody had to copy a secret.

## Signing in as a user (the CLI)

Not every caller is a server. The [CLI](/dev/cli/overview) acts **as you**, across
every organization you belong to, so it signs in rather than carrying a key:

```bash theme={null}
custral login
```

That stores a session on your machine and picks the workspace to act against.
`custral logout` ends it.

<Note>
  Session auth exists for Custral's own clients, the web app, the desktop and
  mobile apps, and the CLI. It is tied to a signed-in person, it expires, and its
  shape is an internal detail that changes without notice.

  **Build integrations on API keys and `/v1`.** That's the surface with scopes, a
  stable contract, and versioning. If you're reaching for a session token to do
  something `/v1` can't, email [hello@custral.com](mailto:hello@custral.com).
  That's a gap worth closing properly.
</Note>

## Errors

* `invalid_api_key`: the API key is missing, unknown, inactive, or expired.
* `insufficient_scope`: the key lacks the scope the endpoint requires.
* `authorization_missing`: no session token was sent (session auth).
* `authorization_invalid`: the token does not match an active session.
* `organization_invalid`: the user behind the session is not a member of the organization in the request.

See [Errors](/dev/errors/overview) for the full catalog.


## Related topics

- [API Reference](/dev/api-reference/overview.md)
- [Applications & API keys](/dev/applications.md)
- [Settings](/start/settings/overview.md)
- [MCP Server](/dev/mcp/overview.md)
- [Requests & responses](/dev/api-reference/requests.md)
