Identity and access
Who can reach a piece of memory, and where each access decision is enforced.
This page explains who can reach a piece of information and where that is enforced. Agent Memory decides access at three layers: which tenant, workspace and application a credential can reach, which operations it may perform, and who per-record visibility allows to see a memory.
Credentials
| Credential | Who uses it? | How is it sent? |
|---|---|---|
API key (amk_…) | Agents, SDKs, server applications | Authorization: Bearer amk_… |
| External identity provider JWT | Enterprise integrations (if configured) | Authorization: Bearer <jwt> |
| Console session | Human users (console) | Cookie; sign in with Google, Microsoft/OIDC or an email code |
| Development token | Scripts, Development environment only (running locally) | POST /dev/auth/token |
Only a hash of an API key is stored; the key is shown once, when it is created. A key can be given an expiry date and can be revoked from the console; a revoked key gets 401.
API key scope
A key is bound to:
| Binding | Effect |
|---|---|
| A single workspace | Any request with a different workspaceId returns 404. |
| Optionally, a single application | A key bound to an application cannot request another applicationId (404). If no application is set, the key works with the shared pool. |
| Permissions | read, write, feedback, curate, delete, share, promote. Console defaults: read, write, feedback, curate on; delete, share, promote off. |
| Bound agents (optional) | Which agent names the key may act as. The first one is the default. A key with no bound agents acts under its own identity and cannot claim another agentId. |
| Act for users | Required to send a userId. |
In the console, keys are created on the API Keys page. An operation the key lacks permission for returns 403.
Agent and end-user identity
-
Don't send
agentId. The key's default agent performs the operation. If the key is bound to several agents, pass the agent you are acting as. A name the key is not bound to:{"title":"policy_denied","status":403, "detail":"Policy denied assert_identity: this credential is not bound to agent 'docs-agent-b' (identity-binding).", "code":"policy_denied","action":"assert_identity","rule":"identity-binding"} -
userIdis the person the memory is about. To send it, the key needs the act for users permission; otherwise you get the same kind of403.
Memory visibility
visibility | Who sees it? | Note |
|---|---|---|
workspace (default) | Any identity with access to the workspace. | |
private | Only the agent that wrote the memory. | Another agent's key in the same workspace does not see it in search, cannot read it by ID and cannot delete it (404). |
agent | The agent that owns the memory. | Currently behaves the same as private. |
user | Only calls reading on behalf of that userId. | |
tenant | Any identity with access to the workspace. | Does not carry over to other workspaces; for tenant-wide sharing use the rule flow below. Writing it requires the promote permission. |
Visibility is applied in every search leg before ranking; a memory you cannot see never appears with its content in any result, explanation or relationship chain (in explanations it shows up as redacted: true). Tenant admins are exempt from visibility rules.
Deduplication does not merge memories with different visibility; derived memories (e.g. consolidation facts, abstractions) take the most restrictive visibility of their sources.
Sharing
Visibility can only be widened (e.g. private → workspace):
curl -s -X POST "$API/v1/memory/$ID/share" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
-d "{\"workspaceId\":\"$WS\",\"visibility\":\"workspace\"}"
# {"id":"…","visibility":"workspace"}This requires the share permission and ownership of the memory (promote for tenant). Attempting to narrow visibility returns 422. MCP: share_memory(memoryId, visibility). Every share is recorded in the history, the change feed (share) and as a webhook event.
Don't confuse filters with access control
| This is access control | This is a filter |
|---|---|
| Tenant boundary (row-level security in the database + query filters) | Searching with userId: narrows to that user's memories. Without userId, memories with workspace visibility are returned regardless of which user they belong to. |
| The key's workspace and application binding | applicationId: the application's memories + the shared pool. The shared pool is open to every application. |
Permissions (read … promote) | facet and tags: narrowing labels. |
Memory visibility (private, agent, user) | runId: a namespace; anyone in the workspace can name any run. |
| Agent and user identity binding | classification: stored and returned, but currently not part of access decisions. |
If you want to protect an end user's data from other users, use visibility: "user"; passing a userId alone provides no protection.
404 or 403?
| Situation | Response |
|---|---|
| Unknown workspace/application or one belonging to another tenant; a workspace you have no access to | 404 (existence is not leaked) |
| A memory not visible to you (visibility, workspace boundary) or a quarantined memory | 404 |
| The credential lacks the relevant permission | 403 (no body) |
| Policy denial on a visible memory (e.g. sharing a memory you don't own) | 403 policy_denied |
| Acting as an agent or user the credential is not bound to | 403 policy_denied (identity-binding) |
| Missing, invalid, expired or revoked credential | 401 |
Organization, members and roles
- Organization members are invited from the console (
Settings → Members). Roles:tenant-adminandmember. Members are granted access per project (workspace). - A tenant admin performs administrative operations such as approving promotions, sharing, export, deleting projects and settings; tenant admins are exempt from memory visibility.
Tenant-wide rules (sharing across workspaces)
The only channel for information across workspaces is tenant-wide rules:
- When the same decision or approved pattern appears in at least two different projects, consolidation produces a candidate.
- A tenant admin approves or rejects the candidate on the console's Promotions page. It can optionally be approved as a "floor".
- An approved rule appears in every project of the tenant as
tenantEntries; floors appear in every context astenantFloors. - Candidates expire if no decision is made within 14 days.
Floors are also used during consolidation to flag workspace memories that contradict them.
Tenant isolation and placement
- On the standard tier, tenants are separated in a shared database with row-level security (RLS); application queries additionally apply tenant and workspace filters.
- On the dedicated tier, a tenant's data lives in a separate database. If placement is not ready, requests get
503 PLACEMENT_UNAVAILABLE.
Limits
- Classification (
classification) does not affect access. - Permissions from source systems (e.g. a document's original access list) are not synchronized into memory. The writing application decides the visibility of memories.
- Console members' permissions within a project are currently a fixed set (read, write, feedback, curate, delete).
- With an API key, the
/v1/mcp/<application-name>URL does not select the application; use a key bound to the application.