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

# The knowledge graph

> Nodes, edges, domains and authority: the substrate every other Exo surface reads.

One node, read in full. Returned by [`GET /v1/graph/nodes/{node_id}`](/api-reference/graph/get-graph-node).

```json 200 application/json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
{
  "id": "ingest:4a1e88f0-9c2d-4f31-b7a0-5e6c1d820b93:7d3c9b1e04a2",
  "nodeType": "derived",
  "title": "Pricing working session",
  "domain": "d_19f3",
  "origin": "import",
  "authority": 0.6,
  "createdAt": "2026-07-14T09:12:03Z",
  "frontmatter": {
    "source": "chatgpt-export",
    "content_date": "2026-07-14",
    "promoted_from": null
  },
  "edges": [
    {
      "id": "api:ingest:4a1e88f0-9c2d-4f31-b7a0-5e6c1d820b93:7d3c9b1e04a2:ingest:4a1e88f0-9c2d-4f31-b7a0-5e6c1d820b93:2b8f0c6d91a4:relates_to",
      "edgeType": "similar",
      "neighborId": "ingest:4a1e88f0-9c2d-4f31-b7a0-5e6c1d820b93:2b8f0c6d91a4",
      "direction": "outgoing",
      "weight": 0.71
    },
    {
      "id": "api:ingest:4a1e88f0-9c2d-4f31-b7a0-5e6c1d820b93:7d3c9b1e04a2:ingest:4a1e88f0-9c2d-4f31-b7a0-5e6c1d820b93:e91d3b7a6c25:supports",
      "edgeType": "temporal_preceded",
      "neighborId": "ingest:4a1e88f0-9c2d-4f31-b7a0-5e6c1d820b93:e91d3b7a6c25",
      "direction": "incoming",
      "weight": 0.44
    }
  ]
}
```

## What it is

**Node**, one piece of the subject's content held as a claim that can later be revised (in practice, a conversation, a document section, or a coding-session turn, stored with its own id, provenance and weight).

**Edge**, a typed and directed relation between two nodes (in practice, a row saying "these two are related, this strongly, and here is why we think so").

**Domain**, a cluster of nodes that turned out to sit together (in practice, a topic Exo found by looking at how densely the graph connects, not a tag anyone applied).

**Authority**, how much weight a node carries when Exo retrieves (in practice, a number from 0 to 1: content the person wrote or imported ranks high, narration an agent produced about them ranks low).

The graph is not a second copy of your text with links bolted on. It is the only copy that matters to retrieval, and everything on the Concepts tab reads from it: contradictions are mined from nodes, phases cluster nodes by date, the identity manifold decomposes signals extracted from nodes, and the ExoBrain trains on the graph itself.

## Where it comes from

Nodes and their first edges are written by ingestion. Everything after that is either the background cognition cycle adding structure or you asserting it through the API.

| Source                                                                       | What it writes                                                              |
| ---------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| Ingestion                                                                    | Nodes, similarity edges between close content, first domain assignment      |
| Background cognition cycle                                                   | Domains, phase nodes, temporal edges, proposed edges awaiting your approval |
| [`POST /v1/graph/edges`](/api-reference/graph/assert-edge)                   | An edge you assert yourself, stamped `origin: "api"`                        |
| [`POST /v1/graph/nodes/{id}/supersede`](/api-reference/graph/supersede-node) | A successor node and a `superseded_by` edge                                 |

## Node fields

| Field         | Type           | What it means                                                                                                                                                        |
| ------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`          | string         | Stable node id. Use it everywhere a route asks for `node_id`.                                                                                                        |
| `nodeType`    | string         | `anchor` for high-authority identity content, `derived` for ordinary ingested content, `phase` for a discovered era. The vocabulary is open, so read it as a string. |
| `title`       | string or null | Display label. Null for content that never had one.                                                                                                                  |
| `domain`      | string or null | Id of the domain this node was clustered into, or null before clustering has run.                                                                                    |
| `origin`      | string or null | Where the node came from, for example `import` or `conversation`.                                                                                                    |
| `authority`   | number         | Retrieval weight from 0 to 1.                                                                                                                                        |
| `createdAt`   | string         | The content date the node carries, not the moment you uploaded it. This is also the keyset ordering column.                                                          |
| `frontmatter` | object or null | The node's raw stored metadata, passed through as written.                                                                                                           |
| `edges`       | array          | Up to the 200 strongest edges touching this node.                                                                                                                    |

`createdAt` is worth reading twice. Exo stamps the date the content is about rather than the date it arrived, because a ten-year archive imported on a Tuesday is not ten years of Tuesday. That is what makes [phases](/concepts/phase) line up with real history.

## Edge fields

| Field                          | Type           | What it means                                                                                                                                                                                                                                        |
| ------------------------------ | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                           | string         | Stable edge id.                                                                                                                                                                                                                                      |
| `sourceNodeId`, `targetNodeId` | string         | The two endpoints. On a node-detail response these collapse to `neighborId` plus `direction`.                                                                                                                                                        |
| `edgeType`                     | string         | The kind of relation. Discovered types include `similar`, `temporal_preceded` and `temporal_cross`; session ingestion adds `session_sequence` and `spawned_subagent`; supersession writes `superseded_by`; edges you assert default to `relates_to`. |
| `weight`                       | number         | Strength of the relation.                                                                                                                                                                                                                            |
| `directionality`               | number         | A directional stamp in the range -1 to 1.                                                                                                                                                                                                            |
| `directionalitySource`         | string or null | How the direction was decided, for example `structural`.                                                                                                                                                                                             |
| `rationale`                    | string or null | Why the edge exists, when something recorded a reason.                                                                                                                                                                                               |
| `createdAt`                    | string         | When the edge was written.                                                                                                                                                                                                                           |

## Domain fields

| Field         | Type           | What it means                                                                                                              |
| ------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `id`          | string         | Stable domain id. Pass it to [`GET /v1/graph/nodes`](/api-reference/graph/list-graph-nodes) as `?domain=` to list members. |
| `name`        | string         | The raw stored name.                                                                                                       |
| `domainType`  | string         | The kind of cluster.                                                                                                       |
| `memberCount` | integer        | How many of the effective subject's nodes sit in this domain. Not an org-wide count.                                       |
| `description` | string or null | Generated description, when one exists.                                                                                    |
| `createdAt`   | string         | Detection time, and the keyset ordering column.                                                                            |

Domains are org-level: the clustering runs over the whole organization's graph, and `memberCount` narrows the answer to the subject you are acting as. [`GET /v1/graph/domains/{id}`](/api-reference/graph/get-graph-domain) adds a `bridges` array of node ids that connect this domain to others.

## How to use it

Walk a domain, then drill into the node that looks interesting.

<CodeGroup>
  ```bash cURL theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
  curl "https://api.get-exo.com/v1/graph/nodes?domain=d_19f3&pageSize=25" \
    -H "X-Exo-API-Key: $EXO_KEY"
  ```

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

  r = requests.get(
      "https://api.get-exo.com/v1/graph/nodes",
      headers={"X-Exo-API-Key": os.environ["EXO_KEY"]},
      params={"domain": "d_19f3", "pageSize": 25},
  )
  r.raise_for_status()
  page = r.json()
  for node in page["data"]:
      print(node["id"], node["authority"], node["title"])
  ```

  ```typescript TypeScript theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
  const url = new URL("https://api.get-exo.com/v1/graph/nodes");
  url.searchParams.set("domain", "d_19f3");
  url.searchParams.set("pageSize", "25");

  const r = await fetch(url, {
    headers: { "X-Exo-API-Key": process.env.EXO_KEY! },
  });
  if (!r.ok) throw new Error(`${r.status} ${await r.text()}`);
  const page = await r.json();
  for (const node of page.data) {
    console.log(node.id, node.authority, node.title);
  }
  ```
</CodeGroup>

Every list route on the graph family is keyset-paginated: read `data`, and if `hasMore` is true, send `nextCursor` back as `cursor`. There are no totals. See [Pagination](/platform/pagination).

## Details worth knowing

<AccordionGroup>
  <Accordion title="Why an edge I asserted has a lower weight than I sent" icon="circle-question">
    Edges written through [`POST /v1/graph/edges`](/api-reference/graph/assert-edge) are capped at a weight of 0.4, and the response returns the value actually stored. The cap exists so that a leaked key cannot inject a dominating relation into someone's identity graph: organic evidence always outranks an assertion. The edge is stamped `origin: "api"` so you can tell yours apart later.
  </Accordion>

  <Accordion title="Why a list of edges looks scoped differently from a list of nodes" icon="circle-question">
    Nodes are scoped by ownership: [`GET /v1/graph/nodes`](/api-reference/graph/list-graph-nodes) returns nodes whose owner is the effective subject. Edges are shared across the org, so [`GET /v1/graph/edges`](/api-reference/graph/list-graph-edges) scopes by the ownership of the edge's source node. An edge whose source belongs to a teammate does not appear in your listing even if its target is yours. Filter with `?node=` to get both directions around a node you own.
  </Accordion>

  <Accordion title="Why some domain names look like placeholders" icon="circle-question">
    [`GET /v1/graph/domains`](/api-reference/graph/list-graph-domains) returns the raw stored name, which for older organizations can be an opaque placeholder rather than a topic. The `graph` view of [`GET /v1/recall/{recall_type}`](/api-reference/recall/get-recall) resolves a friendly label at read time from the most central member content. Use the graph family when you want the raw substrate and the recall view when you want something to put in a UI.
  </Accordion>

  <Accordion title="What happens to the graph when I edit a node" icon="circle-question">
    [`PATCH /v1/graph/nodes/{id}`](/api-reference/graph/edit-node) with `content` re-embeds the node and collapses it to one fresh chunk. Re-segmentation into multiple chunks stays a job for the ingest pipeline, so a very long replacement is better sent as a new ingest than as a patch. `metadata` merges into the frontmatter rather than replacing it, and `title` replaces the title outright.
  </Accordion>
</AccordionGroup>

## Limits

* Contradiction screening is skipped on edges you assert in v1. An asserted edge that disagrees with the graph is written anyway, at the capped weight.
* Both endpoints of an asserted edge must be owned by the effective subject. Anything else returns 404 [`node_not_found`](/errors/node_not_found), which is also what a node that does not exist returns, so existence cannot be probed.
* [`GET /v1/graph/nodes/{id}`](/api-reference/graph/get-graph-node) returns up to the 200 strongest edges around a node. It is not a full adjacency dump, and there is no route that returns one.

## Next

<Columns cols={2}>
  <Card title="How a memory is made" icon="git-branch" href="/concepts/memory-lifecycle">
    The full life of one node, from ingest through forget.
  </Card>

  <Card title="Insights and proposals" icon="lightbulb" href="/concepts/insights">
    How new edges get suggested, and why they wait for you.
  </Card>
</Columns>
