Projects and scope
This page explains the two levels at which Alvia organizes memories, and when to use each: projects, which are the access boundary, and scope, which organizes memories inside one project.
Projects
Section titled “Projects”A project is a separate set of memories. Each API key belongs to one project, and a request with that key reaches only that project’s memories.
Use separate projects for things that must never share memories:
- your production and staging environments
- separate products
- customers whose data must be kept apart
You create projects and API keys in the dashboard. The Free plan allows three projects; paid plans have no project limit. The Demo Project a new account starts with does not count toward it. See Limits and allowances.
A project also owns its memory policy: all keys and scopes in that project share the same keyword and meaning exclusions on new writes. Give each set of rules its own project, for example when a support service must exclude health disclosures while another application has a different memory purpose. See Memory policies.
Scope labels each memory with who or what it is about. It has five optional fields:
| Field | Use it for |
|---|---|
organization_id |
A customer organization or tenant in your app |
user_id |
One end user |
application_id |
One of your applications |
agent_id |
One agent |
team_id |
A team inside an organization |
Each value is a string you choose, such as your own database ID for that user. Your application owns these IDs, and Alvia stores them as you send them.
Writes use the exact scope
Section titled “Writes use the exact scope”A write stores its memories under exactly the scope fields you send. The same text sent with two different scopes becomes two separate memories, and formation compares a new fact only with memories in the same scope.
{ "content": "Prefers invoices in euros.", "scope": { "organization_id": "acme", "user_id": "u_4821" }}A write with no scope stores its memories in the project’s unscoped space.
Reads filter by the fields you send
Section titled “Reads filter by the fields you send”A read returns memories that match every scope field you send. Fields you leave out match anything.
| Read scope | Returns |
|---|---|
{ "organization_id": "acme", "user_id": "u_4821" } |
Only memories about that user at Acme |
{ "organization_id": "acme" } |
Every memory for Acme, for any user, agent or team |
| None | Every memory in the project, including unscoped memories |
Use the same IDs on every write and read. A read with a different ID finds nothing.
Where scope goes in a request
Section titled “Where scope goes in a request”- In request bodies (store, import, search, recall and context), send scope as a nested
scopeobject. Top-level fields such as"user_id"are refused. - On
GET /v1/memories, send scope fields as query parameters:/v1/memories?organization_id=acme&user_id=u_4821. That request has no body, so ascopeobject in a body is ignored. - MCP tools take the five fields as separate arguments.
How Alvia compares values
Section titled “How Alvia compares values”Alvia trims each value, collapses any run of whitespace to one space, changes it to lowercase and
applies Unicode NFC normalization before it stores or compares it. Acme and acme are the same
value, and responses return the lowercase form. An empty value counts as not sent.
Each value can be up to 128 characters. Values that start and end with two underscores, such as
__all__, are reserved and refused.