> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.livingbrain.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.livingbrain.com/_mcp/server.

# Authentication

Living Brain requires two headers on every request.

| Header         | Purpose                                                                                                   |
| -------------- | --------------------------------------------------------------------------------------------------------- |
| `x-api-key`    | Authenticates the **caller**. Scoped to an organization; carries the role of the member it was issued to. |
| `x-subject-id` | Identifies the **end user** you're acting on behalf of, as a UUID. Brains are owned by subjects.          |

The split matters: one API key serves your whole application, while `x-subject-id` keeps
each of your users' brains isolated from one another. Pass through a stable identifier from
your own auth system — a user id, not an email.

## Configuring the SDKs

Both clients read from the environment when you don't pass values explicitly, so production
code usually needs no arguments at all.

**`TypeScript`**

```typescript TypeScript
import { LivingBrainClient } from "@livingbrain/sdk";

// Explicit
const client = new LivingBrainClient({
  apiKey: process.env.LIVING_BRAIN_API_KEY!,
  subjectId: "3f2b8c1e-7a4d-4e9b-9c2a-5d6e7f8a9b0c",
});

// Or rely on LIVING_BRAIN_API_KEY / LIVING_BRAIN_SUBJECT_ID
const fromEnv = new LivingBrainClient({ subjectId: "3f2b8c1e-7a4d-4e9b-9c2a-5d6e7f8a9b0c" });
```

**`Python`**

```python Python
import os
from livingbrain import LivingBrain, AsyncLivingBrain

client = LivingBrain(
    api_key=os.environ["LIVING_BRAIN_API_KEY"],
    subject_id="3f2b8c1e-7a4d-4e9b-9c2a-5d6e7f8a9b0c",
)

# Async client, same surface
aclient = AsyncLivingBrain(subject_id="3f2b8c1e-7a4d-4e9b-9c2a-5d6e7f8a9b0c")
```

> **Warn**
>
> A key is a bearer credential for your entire organization. Keep it server-side — never ship
> it to a browser or mobile client. If you need browser access, proxy through your backend.

## Per-user clients

Because the subject is a client-level header, the usual pattern is one client per request,
constructed with whichever user you're serving:

**`TypeScript`**

```typescript TypeScript
function clientFor(userId: string) {
  return new LivingBrainClient({
    apiKey: process.env.LIVING_BRAIN_API_KEY!,
    subjectId: userId,
  });
}
```

**`Python`**

```python Python
def client_for(user_id: str) -> LivingBrain:
    return LivingBrain(
        api_key=os.environ["LIVING_BRAIN_API_KEY"],
        subject_id=user_id,
    )
```

## Roles and permissions

An API key inherits the role of the member it was issued to. The organization creator is the
owner.

| Capability                             | Owner | Admin | Developer | Viewer |
| -------------------------------------- | :---: | :---: | :-------: | :----: |
| Manage organization (rename / delete)  |   ✓   |   —   |     —     |    —   |
| Invite / change roles / remove members |   ✓   |   ✓   |     —     |    —   |
| Request API keys                       |   ✓   |   ✓   |     ✓     |    —   |
| Reveal / rotate / revoke own key       |   ✓   |   ✓   |     ✓     |    —   |
| Revoke any key in the org              |   ✓   |   ✓   |     —     |    —   |
| View org (members, keys, brains)       |   ✓   |   ✓   |     ✓     |    ✓   |

## Failure modes

| Response                | Meaning                                                                |
| ----------------------- | ---------------------------------------------------------------------- |
| `401` `Missing API key` | No `x-api-key` header was sent.                                        |
| `401` `Invalid API key` | The key was rejected — revoked, mistyped, or from another environment. |
| `404`                   | The brain exists but isn't owned by this `x-subject-id`.               |

See [Errors](/docs/errors) for the full error envelope.