> 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.

# How to build with Living Brain

An app built on Living Brain writes material into a brain as it happens and reads pages back when
someone needs an answer. The brain organises, versions and indexes the pages. Your app decides what to
capture, whether a page is enough to act on, and where a question goes when it isn't.

Most of the advice below comes from building the
[Automated X Support Bot](/docs/use-cases/x-support-bot), which answers public questions from one
brain and hands the rest to a person.

Write path: Capture (with an originRef) → Accepted (status pending) → Compiled → Notified (webhook or feed).
Read path: Search → Read the page → Supported? Yes: answer and cite the page. No: hand off to a person.

## Choose how to connect

| Interface       | Use it when                                                                                | Start here                                                                            |
| --------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------- |
| **SDK or REST** | Your own backend writes to and reads from brains. You control every call.                  | [TypeScript](/sdk/typescript), [Python](/sdk/python), [API reference](/api-reference) |
| **MCP**         | An agent you don't control (Claude, ChatGPT, Cursor) should read and write a user's brain. | [MCP with API key](/mcp), [MCP with sign-in](/mcp/sign-in)                            |
| **CLI**         | You're scripting imports, inspecting a brain or testing from a terminal.                   | [CLI](/cli)                                                                           |

All three use the same brains. A user can capture through your app and read the same pages from Claude
over MCP.

## Writing to a brain

### Decide what one brain holds

Most products create one brain per end user. Create more than one when the contents shouldn't mix: a
consultancy might keep one brain per client so that a search for one client can never return another
client's pages. Every call names its `brainId`, so the boundary is enforced on every request.

### Give every capture an `originRef`

Set `originRef` to an ID from the system the material came from, such as a Slack message timestamp or
a document ID. The brain uses it as the capture's identity, so a retry after a timeout updates the
same capture instead of creating a duplicate.

**`TypeScript`**

```typescript TypeScript
await client.captures.capture({
  brainId,
  body: {
    kind: "transcript",
    content: transcriptText,
    label: "Billing launch sync, 14 May",
    originRef: `meet:${meetingId}`,
  },
});
```

**`Python`**

```python Python
client.captures.capture(
    brain_id=brain_id,
    kind="transcript",
    content=transcript_text,
    label="Billing launch sync, 14 May",
    origin_ref=f"meet:{meeting_id}",
)
```

A different `originRef` is a different capture, even if the content looks the same to a person.

### Don't search for what you just wrote

A capture returns as soon as the brain accepts it, before anything is compiled. If your app captures
something and searches for it in the next line, the search won't find it. When you need to know a
capture has landed, watch for it:

* **Webhooks**: subscribe to `brain.page.updated` and react when the page changes. See
  [Change feed and webhooks](/docs/changes).
* **Sources**: list sources by `status` to show a queue, and retry the ones that `failed`. See
  [Tracking ingestion](/docs/capturing#tracking-ingestion).

### Send chat turns through the classifier

For conversational products, send every turn with `captureChatTurn` and let the brain decide what's
worth keeping. Most turns aren't, and a filter you write yourself will drop some that are.

### Fill in the profile early

The [brain profile](/docs/concepts#brain-profile) tells the brain what the user cares about and which
terms mean what. It shapes how later captures are compiled and doesn't reach back to earlier ones, so
set it before the first large import.

## Reading from a brain

### Load the index once, search per question

Give an agent the [prompt index](/docs/search#prompt-index) at the start of a session so it knows what
the brain broadly contains, then search when a question needs something specific. The index is short
and changes slowly, so one load per session is usually enough. Each search is a separate call scoped
to one question.

### Search, then read the page

A search result carries a title, a summary and a match reason. Use them to choose which pages to
open, then read the full page before you answer from it.

**`TypeScript`**

```typescript TypeScript
const hits = await client.workspace.searchPages({
  brainId,
  query: "When does the billing page launch?",
  topK: 5,
});

const pages = await Promise.all(
  hits
    .filter((h) => h.status === "active" || h.status === "contested")
    .slice(0, 3)
    .map((h) => client.workspace.getPage({ brainId, slug: h.slug })),
);

// Each page has body, status, version and the sources it was built from.
```

**`Python`**

```python Python
hits = client.workspace.search_pages(
    brain_id=brain_id,
    query="When does the billing page launch?",
    top_k=5,
)

pages = [
    client.workspace.get_page(brain_id=brain_id, slug=h.slug)
    for h in hits
    if h.status in ("active", "contested")
][:3]

# Each page has body, status, version and the sources it was built from.
```

The Automated X Support Bot searches the question as asked plus up to two rewrites, keeps the best eight pages and reads
four in full. Rewrites help most when users phrase things differently from the material, for example
asking "is it free?" when the page is titled "Pricing".

### Rank by similarity, decide by reading

`similarity` orders results. It doesn't tell you whether a page answers the question. Use
`minSimilarity` to drop weak matches, and decide whether you have an answer by reading the page. See
[How search ranks pages](/docs/search-ranking).

### Check the page status

Only `active` pages are settled. A `contested` page holds two claims that disagree. Either show both
claims or hand the question to someone who can [resolve](/docs/pages) it. Skip `deprecated` and
`archived` pages unless the question is about history.

## Deciding what to do with the evidence

### Answer from pages, and say which ones

Pass the pages you read to your model and ask it to answer from them only. Ask it to return which page
supports each claim. Then keep the page `slug` and `version` with the answer, so anyone can later see
exactly what the answer was based on.

### Give unsupported questions somewhere to go

Some questions won't be covered by the brain. Decide in advance where those go: a support queue, a
Slack channel, a "we don't know yet" reply. The Automated X Support Bot sends these to a Slack
channel with the reason and what the brain was missing.

### Don't fall back on the model's own memory

When search returns nothing useful, a model asked to answer will usually still produce a fluent
reply, and a reader can't tell it apart from one backed by pages. If retrieval fails, escalate.

### Check claims against the pages

For anything public or high-stakes, run a second check after drafting. Confirm every page the draft
cites exists and says what the draft claims. The support bot does this in code (references, length) and with
a separate model call (does each claim follow from the pages). It allows one revision that can only
remove or soften claims. If the checks still fail, it escalates.

## Keeping a brain healthy

* **Watch [Strain](/docs/brain-health#strain).** Rising Strain means failed sources, a backlog or
  unresolved conflicts. Each of those can make answers worse without any error showing up.
* **Resolve contested pages.** Until a person decides, every reader sees the disagreement.
* **Fill gaps you find.** When a question is escalated because the brain had nothing, capture the
  answer once a person gives it. Search will then find that page for later questions phrased other
  ways.

## Before you ship

* Every capture sets `originRef`.
* Nothing searches for a capture in the same request that sent it.
* Answers are built from full pages, not search snippets.
* `contested` pages are shown as disputed or escalated.
* Every answer records the page slugs and versions it used.
* There's a defined place for questions the brain can't answer.
* Retrieval failure leads to escalation, not a guess.