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

# Belief and supersession

> Exo never overwrites a belief. It records what replaced it, when, and how much authority moved.

The revision trail of one belief. Returned by [`GET /v1/graph/nodes/{node_id}/history`](/api-reference/graph/get-node-history).

```json 200 application/json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
{
  "nodeId": "ingest:4a1e88f0-9c2d-4f31-b7a0-5e6c1d820b93:7d3c9b1e04a2",
  "title": "Pricing working session",
  "chain": [
    {
      "edgeId": "ingest:4a1e88f0-9c2d-4f31-b7a0-5e6c1d820b93:7d3c9b1e04a2_ingest:4a1e88f0-9c2d-4f31-b7a0-5e6c1d820b93:91c7d0e35b4f_superseded_by",
      "predecessorId": "ingest:4a1e88f0-9c2d-4f31-b7a0-5e6c1d820b93:7d3c9b1e04a2",
      "successorId": "ingest:4a1e88f0-9c2d-4f31-b7a0-5e6c1d820b93:91c7d0e35b4f",
      "createdAt": "2026-07-14T09:12:03Z"
    }
  ],
  "versions": [
    {
      "versionId": "3f6c1d7a90b24e5aa1c8e2740b93df15",
      "changeType": "supersede",
      "changedBy": "api",
      "rationale": "Seat plus usage won after the November renewals.",
      "createdAt": "2026-07-14T09:12:03Z"
    }
  ],
  "audit": [
    {
      "action": "supersede",
      "actorKeyHash": "9f2c4b6a1d8e3f07",
      "createdAt": "2026-07-14T09:12:03Z"
    }
  ]
}
```

## What it is

**Supersession**, the record that one belief replaced another (in practice, a directed link from the old node to the new one, plus the authority that moved with it).

Most stores treat a changed mind as an update: the old text is gone and the new text is all that remains. Exo treats it as an event. The old node stays, the new node is linked to it as its successor, and the old node's authority decays so retrieval stops preferring it. Nothing is destroyed, so you can still answer "what did they used to think, and when did that change".

That property is what makes the rest of the product possible. [Contradictions](/concepts/contradiction) are only meaningful if the losing side is still on record, and a [Phase](/concepts/phase) is only readable if beliefs carry the dates they held.

## Where it comes from

Supersession is explicit. Nothing supersedes automatically on ingest; you call the route when you know one belief replaced another.

```mermaid theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
flowchart LR
  A[Old belief node] --> B[POST /v1/graph/nodes/id/supersede]
  B --> C[Successor node<br/>new content, or an<br/>existing node you own]
  B --> D[superseded_by edge]
  B --> E[Old node authority<br/>decays by 1 - confidence]
  C --> F[GET .../history<br/>returns the chain]
  D --> F
  classDef focus stroke-width:2px;
  class B focus;
```

## Fields

The history response has three arrays that answer three different questions.

| Field      | Type                 | What it answers                                                                                  |
| ---------- | -------------------- | ------------------------------------------------------------------------------------------------ |
| `chain`    | `SupersessionLink[]` | What replaced what. Ordered oldest link first, so you can walk backwards to the original belief. |
| `versions` | `HistoryVersion[]`   | What changed on this node, why, and who said so.                                                 |
| `audit`    | `HistoryAudit[]`     | Which credential performed each action, and when.                                                |

A link in the `chain`:

| Field           | Type   | What it means                                        |
| --------------- | ------ | ---------------------------------------------------- |
| `edgeId`        | string | The `superseded_by` edge, formed from both node ids. |
| `predecessorId` | string | The node that was replaced.                          |
| `successorId`   | string | The node that replaced it.                           |
| `createdAt`     | string | When the replacement was recorded. Nullable.         |

A `versions` entry carries `versionId`, `changeType`, `changedBy`, an optional free-text `rationale`, and `createdAt`. An `audit` entry carries `action`, the truncated `actorKeyHash` of the key that acted, and `createdAt`. The audit trail names a key, never a person.

## How to use it

Record the replacement, then read the trail back.

<CodeGroup>
  ```bash cURL theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
  curl -sS -X POST \
    "https://api.get-exo.com/v1/graph/nodes/ingest:4a1e88f0-9c2d-4f31-b7a0-5e6c1d820b93:7d3c9b1e04a2/supersede" \
    -H "X-Exo-API-Key: $EXO_KEY" \
    -H "Content-Type: application/json" \
    -d '{
          "content": "Pricing is seat price plus usage, agreed after the November renewals.",
          "reason": "Seat plus usage won after the November renewals.",
          "confidence": 0.9
        }'
  ```

  ```python Python theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
  import os, requests
  from urllib.parse import quote

  old = "ingest:4a1e88f0-9c2d-4f31-b7a0-5e6c1d820b93:7d3c9b1e04a2"
  base = "https://api.get-exo.com/v1/graph/nodes"
  headers = {"X-Exo-API-Key": os.environ["EXO_KEY"]}

  r = requests.post(
      f"{base}/{quote(old, safe='')}/supersede",
      headers=headers,
      json={
          "content": "Pricing is seat price plus usage, agreed after the November renewals.",
          "reason": "Seat plus usage won after the November renewals.",
          "confidence": 0.9,
      },
  )
  r.raise_for_status()
  print(r.json()["newNodeId"], r.json()["newAuthority"])

  h = requests.get(f"{base}/{quote(old, safe='')}/history", headers=headers)
  h.raise_for_status()
  for link in h.json()["chain"]:
      print(link["predecessorId"], "->", link["successorId"])
  ```

  ```typescript TypeScript theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
  const old =
    "ingest:4a1e88f0-9c2d-4f31-b7a0-5e6c1d820b93:7d3c9b1e04a2";
  const base = "https://api.get-exo.com/v1/graph/nodes";
  const headers = {
    "X-Exo-API-Key": process.env.EXO_KEY!,
    "Content-Type": "application/json",
  };

  const r = await fetch(
    `${base}/${encodeURIComponent(old)}/supersede`,
    {
      method: "POST",
      headers,
      body: JSON.stringify({
        content:
          "Pricing is seat price plus usage, agreed after the November renewals.",
        reason: "Seat plus usage won after the November renewals.",
        confidence: 0.9,
      }),
    },
  );
  if (!r.ok) throw new Error(`${r.status} ${await r.text()}`);
  const { newNodeId, newAuthority } = await r.json();
  console.log(newNodeId, newAuthority);
  ```
</CodeGroup>

Node ids contain colons, so percent-encode one before putting it in a path segment. `curl` will send the raw id, which works, but a URL builder in your language may not.

### What confidence does

`confidence` sits in `[0, 1]` and drives the authority transfer: the superseded belief decays by `1 - confidence`. The default of `1.0` fully demotes the old node, which is what you want when the replacement is certain. Send `0.6` when you are recording a shift you are not sure has settled, and the old belief keeps enough authority to still surface in retrieval.

The successor does not start from scratch. It inherits the higher of the two authorities, so replacing a high-authority belief with a new note does not quietly downgrade the answer. The response reports the successor's resulting authority as `newAuthority`, so you never have to infer it.

## Details worth knowing

<AccordionGroup>
  <Accordion title="Whether you supply the successor or Exo creates it" icon="circle-question">
    Either, and exactly one. Send `content` and Exo creates a successor node from that text, embeds it and links it. Send `nodeId` and Exo links a node you already own as the successor. Sending both, or neither, is a `422` [`validation_error`](/errors/validation_error), and so is naming the node itself as its own successor. The `created` field on the response tells you which of the two happened.
  </Accordion>

  <Accordion title="Why a chain can be shorter than you expect" icon="circle-question">
    The chain names only nodes the effective subject still owns and has not forgotten. A successor that was tombstoned drops out of the chain rather than leaking its id, so a belief revised three times can come back with two links. This is a privacy property, not a bug: forgetting has to be real even when the forgotten node was part of a history someone else can read.
  </Accordion>

  <Accordion title="What supersession does to the ExoBrain" icon="circle-question">
    It dirties it. Every mutation through the public API stamps the org's brain as changed, so the next [`POST /v1/retrieve`](/api-reference/retrieve/post-retrieve) still serves the brain's answer but reports `degraded: true` with `degradationReason: "brain_stale"`. The weights do not know about the new belief until you retrain. See [Brain path and hybrid path](/concepts/retrieval-paths).
  </Accordion>

  <Accordion title="Which scope this needs" icon="circle-question">
    `graph:write`, which the `write` umbrella grants. Reading the history needs `graph:write` too, because the route sits on the mutation router rather than the read one. A `read`-only key gets a `403` [`permission_denied`](/errors/permission_denied) on both.
  </Accordion>
</AccordionGroup>

## Limits

* Supersession is a call you make, not something Exo infers. Ingesting a document that contradicts an earlier one creates a [contradiction](/concepts/contradiction), not a supersession.
* There is no un-supersede. To reverse one, supersede in the other direction, which appends to the chain rather than erasing it.
* The audit trail names the acting key by a truncated hash. It does not name a person, and there is no route that resolves a hash back to a key.
* `rationale` is free text and is never parsed. It is for the human reading the history later.

## Next

<Columns cols={2}>
  <Card title="Contradiction" icon="split" href="/concepts/contradiction">
    The conflicts a supersession is usually resolving.
  </Card>

  <Card title="How a memory is made" icon="repeat" href="/concepts/memory-lifecycle">
    Where supersession sits between editing and forgetting.
  </Card>
</Columns>
