Skip to main content
If you have built on a memory layer before, most of what you know transfers. You add content, you scope it to a user, you search it back, you delete on request. Exo does all four. This page maps that vocabulary onto Exo’s routes and is specific about the three places where the model is genuinely different.
Everything below describes Exo’s own API, verified against the published contract. It does not make claims about any other product’s current behaviour. Check their documentation for theirs, then compare against what is written here.

Before you start

A key with write

Migration writes content and provisions subjects.

Your export in hand

Whatever your current layer gives you. Exo ingests plain text.

A user-id mapping

One subject per end user. Your existing user ids usually work as-is.

The vocabulary map

The two habits worth unlearning:
  • Scoping is a header, not a body field. X-Exo-Subject selects whose partition you read and write. A user_id in the JSON body does not do it, and nothing warns you: the call silently acts as the key owner.
  • Writes are asynchronous. Every ingest returns 202 with a jobId. The content is durably queued, not yet searchable. Poll GET /v1/ingest/{jobId}/status, or subscribe to job.completed.

The shortest call that works

Your “add a memory” becomes this:
202 Accepted

Moving the data

Provision a subject per user

Idempotent, no request body. Run it over your whole user list; re-running is free.
Subject ids allow 1 to 128 characters of A-Za-z0-9._:-. Email addresses do not qualify, because @ is outside that set. Normalize before you start, and keep your own mapping: Exo never returns it.

Batch the content in

POST /v1/ingest/batch takes up to 500 items per call, each with up to 100,000 characters of content plus optional title, source_type and metadata.
Put your old system’s record id into metadata so you can reconcile later:

Read results, not just status codes

A batch with invalid items still returns 202. Per-item outcomes live in results, in request order.
202 Accepted
Note that failed is total minus succeeded, so it includes the skipped items. If you are counting migration losses, read skipped, and read results to find out which of your records did not make it.

Wait for the graph to build

Ingest creates nodes. The connections, domains, identity and the rest are built by a background cycle afterwards, so a migration that finished five minutes ago will not yet answer well.
Watch for every job to reach succeeded, then give the cycle time before you judge retrieval quality. See How a memory is made.

Switch reads over

Point your retrieval at POST /v1/retrieve, and read path and degraded on the first responses. A freshly migrated organization has no trained brain, so expect "path": "hybrid" with "degradationReason": "no_brain". That is the honest baseline, and it is comparable to a vector store. Train a brain to get the reasoning layer. See Training and serving an ExoBrain.

What changes about conflicting facts

This is the difference that will actually affect your code. A memory layer that stores facts has to decide what happens when a new fact disagrees with an old one, and the usual answer is that the newer write wins and the older one is updated or dropped. That keeps reads clean. It also means a reversal, someone changing their mind, is indistinguishable from a correction, and neither is visible to your product. Exo does not overwrite. It keeps both and surfaces the conflict:
Part of a POST /v1/retrieve response
Three consequences to plan for:
  1. Your agent can check before it acts. includeContradictions defaults to true on retrieve, so an agent can refuse to act on a contested belief rather than confidently picking the stale side.
  2. You resolve conflicts explicitly, by superseding the side that lost with POST /v1/graph/nodes/{id}/supersede. The old belief stays in the chain and authority moves to the successor. See Contradictions in review.
  3. status is always "open". v1 stores no resolution lifecycle, so ?status=resolved returns an empty page by design. Track closure on your side.
If you want last-write-wins behaviour, you can have it: supersede with the default confidence of 1.0 on every update, which fully demotes the previous belief. You just have to ask for it rather than get it silently.

What Exo adds

None of these has an equivalent in a store-and-search memory layer, and each is a real object with a real payload:

Cognition

Why the answer, not just what: focal nodes, gate confidence, and whether it reasoned fast or slow.

ConditioningPack

A ready-to-inject system prompt that makes your own LLM reason and write like the subject.

ManifoldState

The axes a person’s thinking actually varies along, discovered rather than assumed.

Honest degradation

path, degraded and degradationReason say when you are on the fallback, instead of quietly returning worse results.

What Exo does not do yet

Stated plainly, so you can check it against your requirements before you migrate rather than after:

When it goes wrong

Next

One memory per end user

Provisioning, selecting and erasing the subjects you just created.

When to use Exo

The honest version of when a plain vector store is enough.