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
Passformat=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 toall_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
Auser_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:
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
Omittingmemory_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_idon 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.
