Skip to main content
Your coding agent already writes a full transcript of every session to local disk. Those transcripts are the densest record of how you actually work: what you tried, what you rejected, what you decided. Exo reads them and turns them into graph structure. This is the fastest way to take an empty organization to a useful one, because the content already exists.

Before you start

  • A key with the write scope. See Scopes.
  • The exo CLI installed and holding that key.
  • Transcripts on disk: ~/.claude/projects/**/*.jsonl for Claude Code, ~/.codex/sessions/**/rollout-*.jsonl for Codex.
A browser cannot read your local disk, which is why this is a CLI path and not an upload form. There is no way to drag a session transcript into a web page, because the file you would be dragging is not one file.

The short version

--source claude, --source codex and --source all all work. Re-runs are dedup safe: every event carries a stable dedup key, so a second backfill over the same transcripts adds nothing.

What backfill actually does

1

Walk the transcripts

The CLI reads each session file, drops noise, and normalises every turn into a session event. Claude Code and Codex go through the same builder, so the two sources produce the same shape and nothing downstream has to know which agent you used. Codex session ids are prefixed codex- for provenance.Redaction happens here, on your machine, before anything leaves it.
2

Send them in batches

Events go to POST /v1/ingest/session-batch, up to 500 per call, with retry and backoff on 429 and 5xx. Because each event carries a dedup key, a retried batch cannot double-write.
3

Finalize once

After the last batch is accepted, the CLI calls POST /v1/ingest/session-finalize exactly once. This is the step that matters, and it is the one people miss when they script this by hand.
Session ingestion writes nodes. It does not, on its own, produce graph edges, domains, the identity extraction or the first cognition cycle. Finalize is what kicks that build. Skip it and you get a pile of session nodes and empty recall surfaces, which looks exactly like a broken import.It is idempotent. Calling it again while a build is running is a no-op and comes back with enqueued: false and alreadyInFlight: true.
4

Watch the layers fill in

Poll building, not warmLayers. building tracks whether a job is actually active. The warm layers are data dependent: a layer can legitimately stay false forever because the content does not support it. Insights, for example, need node pairs that land in a narrow similarity band, and a small organization may produce none.A loop that waits for all five warm layers to go true never exits on some organizations.

The live tail

exo daemon watches for new session activity and streams it as you work, so the graph stays current without a second backfill. It watches the Claude Code root, and optionally a Codex root as well. The daemon and the backfill share one implementation of the event builder, including the noise filter and the redaction pass. They cannot drift apart and start sending differently shaped events.

Reading sessions back

GET /v1/recall/sessions lists the coding sessions in the graph. DELETE /v1/recall/sessions/{session_id} removes one, which is the targeted way to drop a session that captured something it should not have.
To remove a specific memory rather than a whole session, see Forgetting and portability.

Agent signals

POST /v1/ingest/agent-signals records where a coding agent went wrong: the corrections, retries and dead ends that a transcript alone does not label. Those signals are what the AgentFailurePattern object is built from.
The AgentFailurePattern object is defined in the v1 contract and the signal ingestion route is real, but the team-facing surface that reads clustered patterns back is not part of v1. You can send signals today. There is no v1 route that returns the clusters. See AgentFailurePattern for the concept and the honest status.

When it goes wrong

Finalize was never called, or it is still running. Check finalizePending and building on GET /v1/ingest/session-status. If both are false and the warm layers are still empty, call POST /v1/ingest/session-finalize again. It is idempotent, so this is safe.
It should not, and if it did, the events were not carrying dedup keys. That happens when a hand-rolled script posts to POST /v1/ingest/session-batch directly instead of going through the shared event builder. Use exo backfill rather than reimplementing the normaliser.
Expected on a big first import, and the CLI already backs off and retries. If you are posting batches yourself, honour Retry-After on the 429. See Rate limits.
Pass --source codex or --source all. The default source does not include Codex. Codex support also needs an exo CLI release that includes it, and compressed rollouts need the zstandard dependency present.

Next

Claude Code

Connect the client so new work is captured as it happens.

Phase

What Exo builds from the dates on all that history.