Skip to main content
Most memory systems resolve conflicts silently: the newest write wins and nobody finds out that a decision reversed. Exo surfaces the conflict instead and leaves the judgement to you. This guide wires that into a review step, from the event that fires to the supersession that closes it.

Before you start

A key with write

Listing needs read. Closing one needs graph:write, which a write key grants.

Some history

A contradiction needs two beliefs that disagree. One import is rarely enough.

A place to show them

A review queue, a Slack channel, or a pull-request comment.

The shortest call that works

200 OK

Reading a contradiction

a and b are the two poles. What they contain depends on the kind of conflict: tension is a 0 to 1 salience computed when you read, not stored. Retractions score higher than reversals because dropping a belief outright is a stronger signal than changing your mind about which option wins. Sort by it to put the sharpest conflicts at the top of a review queue.
Every contradiction comes back with status: "open". v1 stores no resolution state, so ?status=resolved returns an empty page by design rather than an error. Do not build a UI that waits for a contradiction to flip to resolved: it never will. Track closure on your side, and close the underlying conflict by superseding a belief.
Note also that a Contradiction carries no node ids. a and b are text. Closing one therefore takes an extra step: find the node behind the losing side first.

The review loop

Get told, do not poll

Subscribe to contradiction.detected rather than sweeping the list on a timer. Register a webhook so the review still happens when nobody has the dashboard open.
contradiction.detected is a person-scoped event: it reaches only endpoints registered by the person whose beliefs conflict. See Reacting to change.

Page the open list

Deliveries are thin by design, so fetch current state rather than trusting the payload. Page with nextCursor until hasMore is false.

Show a human both sides

Render a and b side by side with detectedAt and tension. Do not pick a winner automatically. The whole value of surfacing a conflict is that a person decides, and the person usually knows something the graph does not.A useful review card reads: β€œOn 11 July you held Mobile is the 2026 priority. On 14 July you held Defer mobile until the API ships. Which one still stands?”

Find the node behind the losing side

GET /v1/recall/graph/search does a full-text match over stored chunk text and returns node ids, which is what the supersede call needs.
A query with no usable search terms returns an empty list rather than an error, so guard for the empty case before you index into it. GET /v1/graph/nodes?q= is the alternative when you want a title substring match with cursor pagination instead.

Supersede the side that lost

POST /v1/graph/nodes/{id}/supersede records old -> superseded_by -> new. The old belief is not deleted: it stays in the chain, and authority moves to the successor.
200 OK
Send exactly one of content (create a successor from text) or nodeId (link an already-owned node as the successor). Sending both, or neither, is a validation error. Both nodes must belong to the effective subject; anything else is a 404.

Choosing a confidence

confidence drives the authority transfer. The successor inherits the higher of the two authorities, and the superseded belief decays by 1 - confidence. Reach for a value below 1.0 when the old belief may still be right in some context. That is what keeps the chain honest rather than turning supersession into a soft delete.

Contradictions inline with retrieval

You do not have to run a separate review to see conflicts. POST /v1/retrieve returns the ones bearing on the current query in the same response as the answer:
This is the version worth wiring into an agent: before it acts on a belief, it can check whether that belief is contested. includeContradictions defaults to true, so set it to false only when you want the cheaper call.

When it goes wrong

Next

Contradiction

The object itself, why status is always open, and how tension is derived.

Belief and supersession

Authority transfer, and reading a chain from GET /v1/graph/nodes/{id}/history.