Skip to main content
If your app scopes writes and reads by user_id, a space accumulates a per-user history without anywhere to address it directly — you’d otherwise reconstruct it yourself with retrieve and a wide-open query. These two endpoints are that address: one reads back what a space knows about a user, the other asks a question that is answered from that user’s memories only.
Both endpoints require user_id to already be a scope your app uses. There is nothing to create or register up front — see An unknown user is not a 404 below.

Get a user’s profile

Response 200 OK

Prompt-ready profile

Pass format=block to also get a joined, token-budgeted string, ready to drop into a system prompt — the same rendering retrieve uses.

Ask about a user

Runs a synthesis pass over one user’s memories only, and returns a single answer instead of a list. Scope is not a filter you can relax here — it is forced to all_strict, so the answer only ever comes from memories written under this user_id.
Response 200 OK

Two things worth knowing

An unknown user is not a 404

A user_id is not a resource you create — it’s a scope tag stamped onto memories the first time you record with that user_id. There’s no user registry, so there’s no valid set for a typo to fall outside of. A user_id nobody has ever recorded under still returns 200:
A 404 space_not_found is still returned for an unknown space — a space is a real object your account owns, so that stays a real error.

The default view is one layer

Omitting memory_type returns each piece of knowledge about the user once: the consolidated note where one has been synthesized, the raw fact where it hasn’t yet. Consolidation runs on a background loop, so a user active in the last few minutes may show up as raw facts with no note above them yet — that’s expected, not a gap. Pass memory_type=note to see only the synthesized layer. memory_type=fact is not the full evidence set — this endpoint always collapses a fact into its consolidation once one exists, so memory_type=fact here returns only facts that have not been consolidated yet, a set that shrinks toward empty as consolidation catches up. To read every raw fact regardless of consolidation state, use GET /v1/spaces/{space_id}/memories with type=fact&prefer_observations=false. See Memory types for the full set of layers.

The count is not monotonic

Because the default view collapses layers, memory_count can decrease between two reads even though nothing was deleted and more was written in between. Consolidation folds several raw facts into a single note. Before it runs, those facts are counted individually; afterwards they are hidden behind the one note that replaced them, and the count drops. A profile observed during a bulk import can genuinely read 115, then 67 a few minutes later, while the underlying corpus grew the whole time. Nothing is lost. To see the full corpus including every raw fact regardless of consolidation state, use GET /v1/spaces/{space_id}/memories with prefer_observations=false — that total only ever grows. Treat memory_count as “how many distinct things we currently know about this user”, not as an ingestion counter. If you need a stable number to show users or alert on, take it from the uncollapsed list instead.

Scope must match exactly

user_id here has to be the same value your app passes when recording — see Scoping. A profile only sees memories written with that scope, so:
  • A typo’d user_id on either side silently looks like an empty profile, not an error.
  • If your space has history that predates adopting scoping, that older, unscoped history will not appear here. Backfill the scope onto it if these endpoints need to see it too.

Pricing

Error responses

See the full error reference.

Next steps

Scoping guide

How user_id isolates memories inside one space.

Reason API

Synthesize an answer across an entire space, not just one user.

Retrieve API

Rank a space’s memories against a query.

MCP Integration

Read a user’s profile from an MCP-connected assistant.