Skip to main content
Send your key as the X-Exo-API-Key header on every request. There is no session, no login step and no token exchange in front of it.
The same key is also accepted as a bearer token, which is the easier path through HTTP clients and proxies that assume Authorization:
cURL
Send one or the other. Both authenticate identically.

What a key is

A key looks like exo_<env>_<token>, where the middle segment names the deployment it belongs to. A production key is minted exo_prod_...; a test-mode key is minted exo_sandbox_.... The token is 43 URL-safe characters, so treat the whole string as opaque and never split it on anything but the first two underscores. It is an organization credential bound to the user who minted it, and it resolves on every request to a principal: an org, a user, and a set of scopes. GET /v1/me shows you exactly what yours resolves to.
200 application/json
Org and user ids are UUIDs. subjectDefault is a user id, not a subject key you provisioned. subjectDefault is the identity your calls act as when you send no subject selector. It is always the key owner, and it is deliberately not the resolved value of an X-Exo-Subject header, because internal subject ids are an isolation detail that never leaves the API.

Selecting a subject

If you provision a subject per end user of your product, the selector is a header, not a body field:
cURL
Omit the header and the call acts as the key owner, so single-user integrations stay one line. A selector naming a subject that was never provisioned is refused before the handler runs. See One memory per end user.
A handful of routes read the key owner’s own content by construction and refuse an X-Exo-Subject selector outright: POST /v1/brain/query and POST /v1/brain/answer are the ones you are most likely to meet. Each endpoint page says so explicitly.

Minting a key

Your first key comes from the Exo dashboard, because a signed-in dashboard session is allowed to mint. After that, POST /v1/keys mints from the API itself, and an API key must carry the admin scope to use it.
201 application/json
Three things about that response:
  • apiKey is shown once. Only the SHA-256 hash is stored, so nothing can recover the plaintext afterwards. If you lose it, revoke the key and mint another.
  • id is that hash, 64 hex characters. It cannot authenticate. It is the opaque id GET /v1/keys lists and DELETE /v1/keys/{id} takes, so it is safe to log and safe to show in your own dashboard.
  • Omitting scopes inherits the caller’s. Naming a scope the caller does not hold is a 403, so minting can never escalate.
List every umbrella scope the key needs. read, write and admin are separate grants, not a ladder: a key minted with ["write"] alone is refused by every read route, including the job-status poll that follows an ingest. That is why the example above asks for ["read", "write"]. See Scopes.
Sending an Idempotency-Key makes the mint replay-safe, but a replayed response carries a marker in place of apiKey. The plaintext is never persisted anywhere, not even in the replay store, so a retry can return the same key id but never the same secret. Capture apiKey from the first response.

Listing and revoking

GET /v1/keys returns the caller’s keys, newest first, active and revoked. Plaintext is never in this response.
200 application/json
lastUsedAt is the field to watch when a client “looks connected” but Exo has heard nothing from it. A key that stays null was never sent. DELETE /v1/keys/{key_id} soft-revokes and returns 204 with no body. It is idempotent, so revoking an already-revoked key you own is another 204. A key that is not yours, in any sense, is a 404 rather than a 403, which also keeps key hashes unenumerable.
Listing is scoped to the calling user inside their organization. An admin key sees its own keys, not every member’s.

What a leaked key can and cannot do

This is the reason scopes exist, so it is worth stating in one place. A key that escapes into a log, a client bundle or a screenshot can do exactly what its scopes allow inside one organization, and nothing else. The practical consequence: mint narrow keys per workload. A key that only ingests should hold write and nothing more. A key that only serves reads should hold read. Read Scopes for the full vocabulary and what each one unlocks. Revocation is the containment step. DELETE /v1/keys/{id} takes effect on the next request, and lastUsedAt on the remaining keys tells you what is still live.

Sandbox keys

A sandbox key is bound to an isolated partition of your org, so you can run the quickstart, a CI suite or a demo without putting anything into real memory. Mint one with POST /v1/sandbox/keys, which needs admin.
201 application/json
Four things differ from a live key:
  • The prefix is exo_sandbox_, so it is recognisable on sight.
  • Scopes are fixed at read and write and are not caller-selectable. A test key must never be able to mint keys, provision subjects or erase real data.
  • It expires. expiresAt says when. Mint another to continue.
  • It is deliberately not idempotency-replayable. A retried Idempotency-Key mints a fresh key rather than replaying the first response, because persisting a plaintext credential to save one key is the wrong trade.
POST /v1/sandbox/reset wipes the partition and keeps the keys, so your test credential keeps working against an empty graph. Full behaviour on Sandbox.

OAuth, and where it applies

Exo also speaks OAuth 2.1 with PKCE, but not on this API. It exists for MCP clients that cannot hold a static credential: the consumer Claude app on web, desktop and mobile, and the ChatGPT connector. Those clients point at Exo’s MCP endpoint, discover that it is OAuth-protected, send the user to a sign-in page, and receive a minted exo_ key as the access token. One trust root, so revoking the key in the dashboard kills the connection. Nothing about that flow changes how you call /v1/*. If you are writing server-side code, use a static key.

When it goes wrong

Every failure body is RFC 9457 problem+json with a stable code and a requestId. See Errors.

Next

Scopes

The full vocabulary, what each scope unlocks, and the two implications that surprise people.

Quickstart

Put the key to work in five calls.