> ## Documentation Index
> Fetch the complete documentation index at: https://docs.anonalabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Scoping Memory by User, Agent, or Session

> Serve many end users from one space, without their memories mixing, using user_id, agent_id, and session_id.

If your app has more than one end user, you need one space with scoped writes and
reads, not one space per user. This guide walks through why, and how.

## The mistake to avoid

It is tempting to create a space per end user, since a space is already an isolation
boundary. Don't:

* Spaces are capped per plan.
* Memories in different spaces are never synthesized together, so you lose the
  cross-conversation, cross-session learning a memory layer exists to give you.

One space, plus a scope key on every call, is the pattern.

## Write under a scope

Add `user_id` (or `agent_id`, or `session_id`) to `record`:

```python theme={null}
client.record(
    space_id="support",
    content="Prefers email over phone.",
    user_id="alice",
)
```

```python theme={null}
client.record(
    space_id="support",
    content="Escalates billing questions to a human.",
    user_id="bob",
)
```

<Note>
  `user_id` is a real field, not `metadata`. Putting a user id in `metadata` stores it
  but does **not** isolate anything. `metadata` is returned with results, never
  filtered on.
</Note>

## Read under the same scope

Pass the identical key to `retrieve` (or `reason`, or the chat proxy):

```python theme={null}
results = client.retrieve(
    space_id="support",
    query="how should we contact them?",
    user_id="alice",
)
# Only Alice's memories come back. Bob's never appear here.
```

## What "strict" means in practice

* A scoped search returns only memories written under the **same** scope. Never
  another user's, and never an unscoped memory either.
* If a space already has history and you turn scoping on today, that old history
  stays visible to unscoped searches but invisible to scoped ones. Backfill the
  scope onto it if scoped callers need to see it.
* Passing more than one key ANDs them: `user_id` + `session_id` returns only that
  user's memories from that specific session.
* Consolidated synthesis (`note` memories) is built **per user**, so a synthesis
  never blends two users' facts, while still rolling a user's sessions up together
  so the memory improves across conversations, not just within one.

## Choosing which key

| Key          | Use for                                                                                                                                                                              |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `user_id`    | The end user of your product. Almost always the one you want.                                                                                                                        |
| `agent_id`   | Which of *your* agents or bots produced the memory, when you run more than one.                                                                                                      |
| `session_id` | One conversation or run, when you need per-conversation isolation *within* a user (rare: most apps want memory to persist across sessions, which is what `user_id` alone gives you). |

## In the drop-in proxy

The same scope keys work as tunables on the chat/responses/messages proxy, as a
body field or a header:

```bash theme={null}
curl https://api.anonalabs.com/v1/chat/completions \
  -H "Authorization: Bearer anona_live_YOUR_KEY" \
  -H "X-Anona-Space-Id: support-bot" \
  -H "X-Anona-User-Id: alice" \
  -H "Content-Type: application/json" \
  -d '{"model": "gpt-4o-mini", "messages": [{"role": "user", "content": "What plan am I on?"}]}'
```

See [Migrating to the drop-in proxy](/guides/migrating-to-proxy) if you haven't wired
that up yet.

## Reserved tags

Tags beginning with `anona:` are rejected with `422 reserved_tag`. Scope is
implemented as tags internally, and this stops a hand-written tag from forging
someone else's scope. Always use the `user_id`/`agent_id`/`session_id` fields, never
a tag that looks like one.

## Reading back one user's history

Once you're writing under `user_id`, [User Profiles](/api-reference/user-profiles) is
the direct way to read it back — everything a space knows about one user, or a
question answered from that user's memories only, without hand-rolling a wide
`retrieve` call yourself. It uses the same `user_id` scope described above, so it only
ever sees what was recorded under it.

## Next steps

<CardGroup cols={2}>
  <Card title="User Profiles" icon="id-card" href="/api-reference/user-profiles">
    Read back everything a space knows about one scoped user.
  </Card>

  <Card title="Retrieve API" icon="magnifying-glass" href="/api-reference/retrieve#scoping-within-a-space">
    The full scoping reference, including tag filters.
  </Card>

  <Card title="Core Concepts" icon="book" href="/concepts">
    Spaces vs. scope, in one page.
  </Card>
</CardGroup>
