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

# Contradiction

> The two sides of a belief conflict Exo found in a subject's own history, and how tense the conflict is.

A surfaced conflict between two things the same subject believed. Returned by [`GET /v1/contradictions`](/api-reference/insights/list-contradictions).

```json 200 application/json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
{
  "data": [
    {
      "id": "cc_51a0f8d3b62e947c",
      "a": "usage-based pricing only",
      "b": "seat price plus usage",
      "tension": 0.75,
      "status": "open",
      "detectedAt": "2026-07-14T09:12:03Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}
```

## What it is

**Contradiction**, a pair of beliefs from one subject's own content that cannot both be current (in practice, a place where they changed their mind and the older position is still sitting in the graph).

Exo does not resolve these. It finds them and hands them to you with both sides intact, because which side is right is a judgement about the subject's intent, not about the text. An assistant that knows a user once committed to usage-based pricing and later argued for seats can say so. An assistant that silently retrieved whichever chunk scored higher cannot.

## Where it comes from

Contradictions are mined from the subject's stored content during the background cognition cycle, not computed per request. Two kinds are detected.

```mermaid theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
flowchart LR
  A[Stored content] --> B[Cognition cycle<br/>mines the text]
  B --> C[Retraction<br/>an explicit walk-back]
  B --> D[Reversal<br/>concept X replaced by Y]
  C --> E[Contradiction rows]
  D --> E
  E --> F[GET /v1/contradictions]
  E --> G[contradictions on<br/>POST /v1/retrieve]
  classDef focus stroke-width:2px;
  class E focus;
```

A **retraction** is a statement that walks back an earlier one, found through the language the subject used to walk it back. A **reversal** is a swap of one concept for another in the same role, for example one datastore replacing another as the chosen option.

## Fields

| Field        | Type   | What it means                                                                                                                   |
| ------------ | ------ | ------------------------------------------------------------------------------------------------------------------------------- |
| `id`         | string | Stable identifier, `cc_` followed by 16 hex characters derived from the pair. The same conflict keeps the same id across reads. |
| `a`          | string | One pole. For a reversal the concept that was replaced; for a retraction the earlier belief text.                               |
| `b`          | string | The other pole. For a reversal the concept that replaced it; for a retraction the later belief text.                            |
| `tension`    | number | Salience from 0 to 1, derived at read time rather than stored. Higher means the conflict is sharper.                            |
| `status`     | string | Always `open` in v1. See [Limits](#limits).                                                                                     |
| `detectedAt` | string | RFC 3339 timestamp of when the cycle found the conflict.                                                                        |

### How tension is derived

`tension` is computed when you read, not stored, so the same conflict can come back with different numbers on different routes. That is deliberate: on a retrieval the number also reflects whether the conflict touches the answer you just asked for.

| Where you read it                                                                    | Retraction | Reversal |
| ------------------------------------------------------------------------------------ | ---------- | -------- |
| [`GET /v1/contradictions`](/api-reference/insights/list-contradictions)              | `0.75`     | `0.55`   |
| [`POST /v1/retrieve`](/api-reference/retrieve/post-retrieve), node not in the answer | `0.75`     | `0.55`   |
| [`POST /v1/retrieve`](/api-reference/retrieve/post-retrieve), node in the answer     | `0.85`     | `0.65`   |

Retractions score above reversals because an explicit walk-back is stronger evidence of a changed mind than a substituted concept.

## The two shapes, and which one you are holding

This is the trap on this object. The array on a retrieval is **not** the `Contradiction` schema above.

<CodeGroup>
  ```json GET /v1/contradictions theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
  {
    "id": "cc_51a0f8d3b62e947c",
    "a": "usage-based pricing only",
    "b": "seat price plus usage",
    "tension": 0.75,
    "status": "open",
    "detectedAt": "2026-07-14T09:12:03Z"
  }
  ```

  ```json POST /v1/retrieve theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
  {
    "kind": "retraction",
    "subkind": "explicit_reversal",
    "node_id": "ingest:4a1e88f0-9c2d-4f31-b7a0-5e6c1d820b93:7d3c9b1e04a2",
    "in_active_subgraph": true,
    "marker": "we moved off",
    "before": "usage-based pricing only",
    "after": "seat price plus usage",
    "tension": 0.85,
    "snippet": "We moved off usage-based pricing only after the November renewals."
  }
  ```
</CodeGroup>

The list route returns the stable camelCase object with an `id` you can store. The retrieval array returns the mining shape in snake\_case, with no `id` and no `status`, plus `node_id` so you can drill into the source and `in_active_subgraph` so you know whether the conflict actually touched this answer. For a reversal it also carries `from_concept` and `to_concept`.

Use the retrieval array to warn in the moment. Use the list route when you need something you can reference later.

## How to use it

Surface the conflict alongside the answer instead of picking a side.

<CodeGroup>
  ```bash cURL theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
  curl -sS "https://api.get-exo.com/v1/contradictions?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/contradictions",
      headers={"X-Exo-API-Key": os.environ["EXO_KEY"]},
      params={"pageSize": 25},
  )
  r.raise_for_status()

  for c in r.json()["data"]:
      if c["tension"] >= 0.7:
          print(f"{c['id']}: {c['a']!r} vs {c['b']!r}")
  ```

  ```typescript TypeScript theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
  const r = await fetch(
    "https://api.get-exo.com/v1/contradictions?pageSize=25",
    { headers: { "X-Exo-API-Key": process.env.EXO_KEY! } },
  );
  if (!r.ok) throw new Error(`${r.status} ${await r.text()}`);
  const { data } = await r.json();

  for (const c of data) {
    if (c.tension >= 0.7) {
      console.log(`${c.id}: ${c.a} vs ${c.b}`);
    }
  }
  ```
</CodeGroup>

To close one, supersede the side that lost with [`POST /v1/graph/nodes/{node_id}/supersede`](/api-reference/graph/supersede-node). That records the newer belief as the successor and transfers authority away from the older one, which is how Exo represents a resolved conflict. See [Belief and supersession](/concepts/belief-and-supersession).

## Details worth knowing

<AccordionGroup>
  <Accordion title="Why every contradiction says open" icon="circle-question">
    Because the v1 store has no resolution lifecycle. `status` is a fixed `open` on every row, so it carries no information today. It exists so a later release can report a resolved conflict without a breaking change. Do not build a filter on it, and do not show it as a state the user can toggle.
  </Accordion>

  <Accordion title="Why the array on a retrieval can be empty" icon="circle-question">
    Three ordinary reasons. The subject has no mined contradictions yet, because the background cycle has not run over their content. The query did not activate any node that carries one. Or you sent `includeContradictions: false`. An empty array is not an error and does not mean the graph is consistent.
  </Accordion>

  <Accordion title="How many come back at once" icon="circle-question">
    The retrieval selector caps what it attaches: at most one per node, at most three per subkind, and at most eight in total. It prefers conflicts on nodes that are in the answer, then fills from recent rows. The list route paginates normally instead, so use it when you want the full set.
  </Accordion>

  <Accordion title="Whether a contradiction survives forgetting" icon="circle-question">
    It drops out. The selector reads live content, so tombstoning a node with [`DELETE /v1/graph/nodes/{node_id}`](/api-reference/graph/forget-node) removes the conflicts that rest on it from the next read. Restoring the node within the undelete window brings them back, because nothing about the conflict was stored separately.
  </Accordion>
</AccordionGroup>

## Limits

* `status` is always `open`. There is no route that resolves a contradiction directly; you resolve one by superseding a side.
* Contradictions are mined from a subject's own content. Exo does not compare one subject against another, so it will not tell you that two people on a team disagree.
* The array on `POST /v1/retrieve` is typed as an open object in the contract, so a generated client hands you an untyped map. The field list above is what the route emits today.
* Detection is language-driven. A belief someone changed without ever writing down the change is not detectable, and will not appear.

## Next

<Columns cols={2}>
  <Card title="Belief and supersession" icon="git-branch" href="/concepts/belief-and-supersession">
    How to close a conflict by recording what replaced what.
  </Card>

  <Card title="List contradictions" icon="split" href="/api-reference/insights/list-contradictions">
    The route that returns the stable form, with pagination.
  </Card>
</Columns>
