Skip to main content
POST /v1/import takes a file, stores it, and hands it to a background worker that parses, chunks, embeds and links it. POST /v1/import/transcript does the same for a conversation where you only want one speaker’s contributions attributed to the subject.

Before you start

  • A key with the write scope. See Scopes.
  • The file on disk, or in memory as bytes.
  • For a per-end-user product, the subject provisioned first. See One memory per end user.

The shortest call that works

Do not set Content-Type by hand. Every HTTP client above sets multipart/form-data with the correct boundary for you, and overriding it produces a body the server cannot parse.

What you can import

The contract publishes no maximum file size, so this site does not either. Chunk your own uploads if you have an unusually large archive, and watch the job rather than assuming.
If you are holding a string rather than a file, do not write it to a temp file to import it. Use POST /v1/ingest instead, which takes JSON. See Ingesting content.

Importing a transcript

A meeting transcript contains several people. You usually want one person’s thinking in the graph, not everyone in the room. target_speaker names whose turns are attributed to the subject. The rest of the file still informs the parse, it is just not recorded as the subject’s belief.
target_speaker is required on this route. Match the speaker label as it appears in the file.

Watching the job

Import returns 202 and nothing is in the graph yet. Poll until the job reaches a terminal status.
Break on “no longer in progress”, not on one spelling of success. This route reports the worker’s own status vocabulary, where a finished job is done. Only GET /v1/jobs/{jobId} renames it to succeeded. A loop that waits for succeeded here never terminates.
Polling is the simple path. For anything long running, subscribe to job.completed instead of holding a loop open. See Reacting to change.

Import history

GET /v1/import returns the 20 most recent imports for the effective subject, newest first, as a flat array with no envelope.
nodesCreated and domainsDetected are null until the worker writes its result. completedAt is null until the job reaches a terminal status.
This route reports the worker’s status vocabulary, the same one GET /v1/ingest/{jobId}/status uses: pending, running, done, failed. A finished import is done, not succeeded. Only GET /v1/jobs/{jobId} renames it. Filter this list on done or your successful imports look like they are still in flight.
A row with status: "done" and nodesCreated: null should not happen, and is worth a support ticket with the request id.

After a large import

A big export creates a lot of nodes at once, and the layers built on top of those nodes are not instant. Identity extraction, the manifold, domains, insights and temporal phases are produced by a background cognition cycle that runs after the content lands. So the sequence a first-time developer actually sees is: import succeeds, POST /v1/retrieve starts returning sources, and GET /v1/recall/identity is still empty for a while longer. That is expected. See How a memory is made.

When it goes wrong

Corrupt files and embedding failures are terminal on the first attempt rather than retried, because retrying a file that cannot be parsed only burns the queue and delays everything behind it. Fix the file and import again. errorMessage on the status response names the reason.
The PDF probably has no text layer. Extraction reads embedded text, so a scanned document is a stack of images as far as the parser is concerned, and it produces no chunks. Run it through OCR first and import the result.
Either the key lacks write, or the X-Exo-Subject value is not provisioned. Exo refuses an unknown subject selector rather than defaulting to the key owner, so an import can never silently land in the wrong person’s partition. See permission_denied.
Usually a missing file part, a hand-set Content-Type that broke the multipart boundary, or a missing target_speaker on the transcript route. The problem body’s errors[] names the field. See validation_error.
Send an Idempotency-Key next time and a retry returns the original job id. If duplicates already landed, forget the extra nodes with DELETE /v1/graph/nodes/{id}, which erases their content and tombstones the node. See Forgetting and portability.

Next

Coding session history

A different shape of import: transcripts already on your disk.

Ingesting content

The JSON routes, for content you already hold as a string.