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

# Brain path and hybrid path

> What path, degraded and degradationReason actually mean on a retrieve response, and how to read them together.

Four fields on every retrieval that say which engine answered and whether you should trust it as much as usual. Returned by [`POST /v1/retrieve`](/api-reference/retrieve/post-retrieve).

```json 200 application/json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
{
  "path": "hybrid",
  "degraded": true,
  "degradationReason": "no_brain",
  "cognition": null,
  "usage": { "readUnits": 8, "embedTokens": 12, "brainInference": false }
}
```

## What it is

Exo has two engines behind one route.

**Brain path**, the org's trained ExoBrain answering from learned weights (in practice, a model that has seen this graph and settled on a region of it).

**Hybrid path**, vector plus keyword search over stored content, fused by reciprocal rank (in practice, ordinary retrieval, run when the brain cannot serve).

You never choose. The route picks, then tells you what it picked. That last part is the design decision worth the page: a memory API that quietly degrades is worse than one that fails, because you cannot tell a thin answer from a wrong one. So `path` names the engine, `degraded` is the one boolean to branch on, and `degradationReason` says why.

## Where it comes from

```mermaid theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
flowchart TB
  A[POST /v1/retrieve] --> B{Org has a<br/>trained brain}
  B -->|no| C[hybrid<br/>no_brain]
  B -->|yes| D{Brain reports<br/>node/edge counts<br/>diverged}
  D -->|yes| E[hybrid<br/>brain_stale]
  D -->|no| F[Brain forward pass]
  F --> G{Graph mutated<br/>through the API<br/>since training}
  G -->|yes| H[brain<br/>degraded true<br/>brain_stale]
  G -->|no| I[brain<br/>degraded false]
  C --> J{Any rows<br/>found}
  E --> J
  J -->|no| K[reason becomes<br/>cold_start]
  classDef focus stroke-width:2px;
  class I focus;
```

Two things in that diagram surprise people. A **stale brain still answers**: staleness is reported, not routed around, because a slightly out-of-date brain is still the best answer available. And **`cold_start` overrides the other reasons**: if the hybrid fallback ran and found nothing at all, the reason you get back is `cold_start` regardless of why you were on the fallback in the first place.

## Fields

| Field                  | Type                    | What it means                                                                                         |
| ---------------------- | ----------------------- | ----------------------------------------------------------------------------------------------------- |
| `path`                 | `"brain"` or `"hybrid"` | Which engine served the call.                                                                         |
| `degraded`             | boolean                 | True whenever the answer is not the best this org can produce. The single field to branch on.         |
| `degradationReason`    | string or null          | `no_brain`, `brain_stale` or `cold_start`. Null only when `degraded` is false.                        |
| `cognition`            | object or null          | The reasoning trace. Present on the brain path, null on hybrid. See [Cognition](/concepts/cognition). |
| `usage.brainInference` | boolean                 | True when a brain forward pass ran. Mirrors `path` and is what you meter against.                     |

### The four states you can actually see

| `path`   | `degraded` | `degradationReason` | What happened                                                                                                               |
| -------- | ---------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `brain`  | `false`    | `null`              | The healthy case. The brain answered and the graph has not moved under it.                                                  |
| `brain`  | `true`     | `brain_stale`       | The brain answered, but the graph was mutated through the API since it trained. The answer is real, the weights are behind. |
| `hybrid` | `true`     | `no_brain`          | No trained brain for this org. Train one, or accept ordinary search.                                                        |
| `hybrid` | `true`     | `brain_stale`       | A brain exists but its node and edge counts diverged far enough that it declined to serve.                                  |
| `hybrid` | `true`     | `cold_start`        | The fallback ran and matched nothing. Usually an empty or still-processing subject, not an engine problem.                  |

`path: "hybrid"` with `degraded: false` cannot happen. Every hybrid response is degraded by definition.

### no\_brain and brain\_stale are different problems

`no_brain` is a setup gap: nobody has trained this org's brain. The fix is [`POST /v1/brain/train`](/api-reference/brain/post-brain-train), once.

`brain_stale` is a maintenance signal: a brain exists and the graph has moved. It is raised two ways. The brain itself reports divergence when node and edge counts drift far enough, which sends you to the hybrid path. Separately, every mutation through the public API stamps the brain dirty, which keeps you on the brain path but sets `degraded: true`. That second mechanism exists because editing a node's text or tombstoning it never changes a count, so a count check alone would call a stale brain healthy.

`cold_start` is neither. It says the search found nothing, so check that the subject has content and that their import jobs have finished before you look at the brain at all.

## How to use it

Branch on `degraded` once, and let `degradationReason` decide what you tell the user.

<CodeGroup>
  ```bash cURL theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
  curl -sS -X POST https://api.get-exo.com/v1/retrieve \
    -H "X-Exo-API-Key: $EXO_KEY" \
    -H "Content-Type: application/json" \
    -d '{"query": "Where did we land on the pricing model?", "topK": 8}'
  ```

  ```python Python theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
  import os, requests

  r = requests.post(
      "https://api.get-exo.com/v1/retrieve",
      headers={"X-Exo-API-Key": os.environ["EXO_KEY"]},
      json={"query": "Where did we land on the pricing model?", "topK": 8},
  )
  r.raise_for_status()
  body = r.json()

  if not body["degraded"]:
      notice = None
  elif body["degradationReason"] == "cold_start":
      notice = "Nothing indexed for this subject yet."
  elif body["degradationReason"] == "no_brain":
      notice = "Running on search. Train a brain for reasoning."
  else:
      notice = "Answer may lag recent changes."

  print(body["path"], notice)
  ```

  ```typescript TypeScript theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
  const r = await fetch("https://api.get-exo.com/v1/retrieve", {
    method: "POST",
    headers: {
      "X-Exo-API-Key": process.env.EXO_KEY!,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      query: "Where did we land on the pricing model?",
      topK: 8,
    }),
  });
  if (!r.ok) throw new Error(`${r.status} ${await r.text()}`);
  const body = await r.json();

  const notice = !body.degraded
    ? null
    : body.degradationReason === "cold_start"
      ? "Nothing indexed for this subject yet."
      : body.degradationReason === "no_brain"
        ? "Running on search. Train a brain for reasoning."
        : "Answer may lag recent changes.";

  console.log(body.path, notice);
  ```
</CodeGroup>

Write the null branch of `cognition` first. On a new org every retrieval is hybrid, so the degraded path is the one your code will exercise before any other.

## Details worth knowing

<AccordionGroup>
  <Accordion title="Whether a degraded answer is a worse answer" icon="circle-question">
    Usually thinner rather than wrong. Hybrid retrieval still ranks the right content; what it cannot give you is the reasoning trace, the focal set, or the brain's own confidence. If your product only shows sources, a degraded response may be indistinguishable to the user. If it shows confidence or explains itself, `degraded` is the field that should change the wording.
  </Accordion>

  <Accordion title="What still works on the hybrid path" icon="circle-question">
    `sources` with full provenance, `contradictions`, `usage`, `traceId`, `latencyMs`, `expand=sources.content` and streaming. Only `cognition` is withheld, because the hybrid path has no reasoning state to report and inventing one would be the exact dishonesty these fields exist to prevent.
  </Accordion>

  <Accordion title="How to clear brain_stale" icon="circle-question">
    Retrain with [`POST /v1/brain/train`](/api-reference/brain/post-brain-train). Training is asynchronous and one train per org may be in flight, so a second call while one is running returns `409` [`conflict`](/errors/conflict). The serve path picks the new brain up on its own once training finishes; you do not restart anything.
  </Accordion>

  <Accordion title="Why cold_start can appear on an org that has plenty of content" icon="circle-question">
    Because it is per call, not per org. It fires when this particular query matched nothing for the effective subject. A wrong `X-Exo-Subject`, a subject who has genuinely ingested nothing, or an import that is still running all produce it. Check [`GET /v1/jobs`](/api-reference/jobs/list-jobs) before assuming the query was bad.
  </Accordion>
</AccordionGroup>

## Limits

* The route decides. There is no parameter that forces the brain path or forbids it.
* `degradationReason` is typed as a plain string. The three values above are what ships today; keep a default branch.
* A `brain_stale` response does not say how stale. `cognition.driftRate` is the closest thing to a magnitude, and it only covers the region this query touched.
* `path` describes the engine, not quality. It is not a confidence score, and it should not be shown to end users as one.

## Next

<Columns cols={2}>
  <Card title="Cognition" icon="brain" href="/concepts/cognition">
    The block that is present on one path and null on the other.
  </Card>

  <Card title="ExoBrain" icon="cpu" href="/concepts/exobrain">
    What the per-org model is, and what training it costs you.
  </Card>
</Columns>
