Before you start
A key that can provision
PUT and DELETE on subjects need subjects:provision. An admin key also qualifies.A stable id per user
1 to 128 characters of
A-Z a-z 0-9 . _ : -. Your own user id works well.Somewhere to store it
Your database maps your user to their subject id. Exo never returns your mapping.
The shortest call that works
Provisioning takes no request body. The id in the path is the whole request.201 Created
Selecting a subject on a call
SendX-Exo-Subject. That header is the selector, on every route that takes one.
The lifecycle
Provision on first sight
Call
PUT /v1/subjects/{id} when the end user first appears in your product, or on every login. It is idempotent, so there is no “already exists” branch to write.Write and read as them
Every ingest, import, retrieve, search, condition and recall call takes
X-Exo-Subject. Their content lands in their partition and never mixes with another subject’s.List and inspect
GET /v1/subjects is keyset-paginated: page with nextCursor until hasMore is false. pageSize is coerced into 1 to 100 and defaults to 25.200 OK
GET /v1/subjects/{id} fetches one, or returns 404 subject_not_found. Use it as an existence check.Erase when they leave
DELETE /v1/subjects/{id} is the documented right-to-be-forgotten path. It returns 204 and is idempotent.Choosing subject ids
The id must be 1 to 128 characters drawn fromA-Za-z0-9._:-. It is yours to choose and it is the only handle you get back, so pick something you already store.
Avoid raw email addresses:
@ is not in the allowed character set, and an id is not the place for personal data you may later have to erase.
A subject id that collides with the key owner is rejected with 409. The key owner is not a subject; it is the identity a request acts as when no selector is sent.
What isolation actually means
Each organization gets its own physical database schema. Subjects live inside it, scoped per user. Two consequences worth designing around:- Cross-subject reads do not happen by accident. A node belonging to another subject returns 404 rather than leaking its existence.
- Any key in your organization can read any subject in it. Subjects isolate your end users from each other, not from you. Your API key is an organization credential, so treat it like one.
GET /v1/me reports subjectDefault, which is always the key owner’s user id: the identity used when no selector is sent. It is deliberately not the resolved X-Exo-Subject value, because internal subject user ids are an isolation detail and never leave the API. Do not use subjectDefault to confirm which subject a call read.
When it goes wrong
Next
Subjects and isolation
The isolation model underneath, and why
subjectDefault is never the resolved selector.Forgetting and portability
Erase one subject completely, or export everything they ever stored.