Skip to main content
The API is in private preview and keys are issued to design partners. Everything on this page is the committed v1 contract, so the calls below are the ones that will work the moment your key is live.
Five calls, in order. Copy each block, run it, and read the response before moving on. The base URL is https://api.get-exo.com everywhere on this site.

Get an API key

Your first key comes from the Exo dashboard: sign in, open Settings, and generate a key. The plaintext is displayed once and stored only as a SHA-256 hash, so copy it before you close the panel. Once you hold a key with the admin scope you can mint further keys from the API itself with POST /v1/keys.Keys look like exo_<env>_<token>. A production key is minted exo_prod_...; a test-mode key from the sandbox family is minted exo_sandbox_... so it is recognisable on sight. Put yours in the environment rather than in source.

Confirm the key with GET /v1/me

GET /v1/me needs no data in the graph, so it is the cheapest proof that your key, your org and your scopes are what you think they are.
200 application/json
scopes is the list your key actually carries, and it is the first thing to check when a later call returns 403.subjectDefault is the identity your calls act as when you send no subject selector. It is always the key owner. It is never the resolved value of an X-Exo-Subject header, because internal subject ids are an isolation detail that does not leave the API.

Ingest a piece of content

POST /v1/ingest accepts the content and returns immediately with a job id. A background worker chunks it, embeds it and links it into the knowledge graph. Send an Idempotency-Key header so a retry returns the original job id instead of enqueuing a second copy.metadata is yours to shape, and two keys are read by the ingest worker: title becomes the node’s title, and source is recorded as the file name it arrived under. Set title and you will see it again in step 5. Leave it out and the node is titled with the first line of your content.
202 application/json
The status is 202, not 200. Nothing is in the graph yet.

Poll the job until it stops moving

GET /v1/ingest/{job_id}/status reports the job’s phase, the phases already completed and a percentage estimate. A generic ingest carries no pipeline block, so phase stays null and completedSteps stays empty. Watch status.
200 application/json
Poll for a terminal state, not for one spelling of success. This route reports the worker’s own status, where the finished value is done. The cross-family view at GET /v1/jobs/{job_id} reports the same job as succeeded, which is the rename the jobs family documents. Break your loop when the status is neither pending nor running and you are correct on both routes.

Retrieve against it

POST /v1/retrieve is the flagship. It returns ranked sources, any contradictions it found, a reasoning block when the org’s trained brain served the call, and a usage block.
200 application/json
The title is the one you sent. Node ids are opaque strings whose shape depends on how the content arrived, so read them, store them and pass them back, but never parse them. This one is content-hashed, which is why re-ingesting identical content updates the same node instead of duplicating it.

What you saw

That last response is worth reading field by field, because four of its fields are the reason Exo is not a vector store with better marketing. The other two values degradationReason can take are brain_stale, meaning the brain is serving a graph that has changed since it trained, and cold_start. Read Brain path and hybrid path for what each one costs you.
The sources and contradictions arrays are open objects in the v1 contract, which is what lets the reasoning layer add fields without a breaking change. The keys shown above are the ones the route emits today. The canonical, listable contradiction object with a stable id, a, b, tension and status lives on GET /v1/contradictions.

When it goes wrong

Every error body is RFC 9457 problem+json and carries a requestId. Quote it if you need help. Full envelope on Errors.
requestId in the error body and the X-Request-Id response header are the id to quote in support. It is minted per request and looks like req_<16 hex>. The traceId in a successful retrieve response is a separate 32-character W3C trace id, propagated from an incoming traceparent header when you send one.

Next

One memory per end user

Provision a subject for each end user of your product, then select it with one header.

Condition your own agent

Turn a subject’s identity into a system prompt any LLM can take.

API reference

All 80 operations, with the traps that the generated schema cannot carry.