Memory API
The memory-api is a per-workspace HTTP service for storing, searching, and managing cross-session memory. In a managed workspace, its URL comes from the Workspace service-group status, not a hard-coded Pod or Service name.
Service endpoints
Section titled “Service endpoints”| Port | Purpose |
|---|---|
8080 | REST API |
8081 | Health and readiness probes |
9090 | Prometheus metrics |
The base path for the REST API is /api/v1. The complete machine-readable
contract is served at /api/v1/openapi.yaml and /docs by the service.
Authentication and scope
Section titled “Authentication and scope”Internal service authentication is enabled by the chart’s
internalServiceAuth.enabled setting. When enabled, callers send a Kubernetes
ServiceAccount bearer token. The service validates it with TokenReview and the
configured subject and namespace allowlists. The runtime and privacy-api
present the projected ServiceAccount token read from SESSION_API_TOKEN_PATH
to memory-api. The facade makes no memory-api calls.
A managed memory-api is already scoped to one workspace. Its tenant check
accepts that workspace’s name or UID, but memory rows are keyed by the
Workspace UID (a UUID column), so a route that stores or matches rows by the
value returns 500 when given the name. Send the UID. When the memory-api has
resolved its Workspace UID, every route that takes a workspace refuses a value
other than its own name or UID with 403. This applies whether the route reads
the workspace query parameter or a workspace_id in the JSON body, including
GET /audit/memories. The response body is:
{"error": "workspace \"<value>\" is not this memory-api's workspace (\"<name>\")"}A route that addresses a memory by its ID matches the ID and the workspace together, so an ID from another workspace is not found.
A memory-api that has not resolved its Workspace UID does not check the workspace. The operator starts every managed memory-api with the workspace and service group, so a managed memory-api always resolves it.
A request that names a user must prove it is that user, or come from a system
service. The runtime sends the conversation’s session token in the
X-Omnia-Subject-Proof header, and memory-api verifies it. Every user-scoped
route checks the user it is given against that proof: the query routes’
virtual_user_id, the scope.virtual_user_id in a write body, and on POST /memories/retrieve the body’s user_id and each structured_lookups[].user_id.
| Request | Result |
|---|---|
| Names the verified user | Served |
| Names a different user from the verified one | 403 |
| Names a user, carries no proof, and the caller is trusted only by namespace | 403 |
| Names a user and the caller is an exact-listed system service | Served |
| Names no user | Not checked |
| Internal service authentication is disabled | Served as scoped |
Query-string routes take these scope parameters; JSON request bodies carry
workspace_id (and, on retrieve, user_id and agent_id). A query-string
route given user_id instead of virtual_user_id returns 400:
| Parameter | Meaning |
|---|---|
workspace | Workspace UID. The tenant check also accepts the name, but a route that matches rows by it returns 500 on a name. |
virtual_user_id | Pseudonymised end-user identity |
agent | Agent scope |
include_shared | GET /memories only: add institutional and agent memories to the user’s own |
workspace is required on every query-string route that takes it, including
GET /audit/memories, except GET /institutional/documents, GET /institutional/documents/{id}/tree and DELETE /institutional/sources/{source}, where omitting it means this memory-api’s own
workspace. POST /institutional/ingest and POST /institutional/ingest/batch
take an optional workspace_id in the body with the same meaning. GET /memory/schema, GET /memory/tool-guidance and the /admin routes take no
workspace.
Memory operations
Section titled “Memory operations”| Method | Path | Purpose |
|---|---|---|
POST | /memories | Create or update a memory |
GET | /memories | List memories in scope |
GET | /memories/search | Search memories |
POST | /memories/retrieve | Multi-tier hybrid retrieval |
POST | /memories/retrieve/semantic | Workspace retrieval with an optional CEL deny filter |
GET | /memories/{id} | Read one complete memory |
PATCH | /memories/{id} | Supersede the memory’s active observation with new content |
DELETE | /memories/{id} | Soft-delete a memory |
DELETE | /memories | Delete all memories for a subject (DSAR) |
DELETE | /memories/batch | Delete up to limit memories for a subject (paginated DSAR) |
GET | /memories/export | Export memories for a subject |
GET | /memories/aggregate | Workspace memory aggregates (Enterprise) |
GET | /memories/conflicts | List entities with more than one active observation |
GET | /memories/{id}/lineage | Read consolidation lineage (Enterprise) |
POST | /relations | Add a relation between memories |
GET | /memory/schema | Discover configured memory types and relation definitions |
GET | /memory/tool-guidance | The LLM memory tools’ descriptions and extra input-schema properties, rendered from the effective MemoryPolicy |
POST /memories/retrieve performs FTS/vector hybrid retrieval when an embedding
provider is available and fuses the rankings with RRF (k=60). Under
Enterprise, POST /memories/retrieve applies the MemoryPolicy tier ranking
(spec.tierPrecedence) and per-tier recency half-life, and queries all tiers.
Without Enterprise it queries only the user and agent tiers, with an identity
ranker and the store’s uniform default half-life. Embedding failures fall back
to FTS-only retrieval.
Institutional and agent-scoped operations
Section titled “Institutional and agent-scoped operations”Institutional routes return 403 enterprise_required on a Core deployment.
They have no access check beyond the ServiceAccount authentication that applies
to every route when internal service auth is enabled. Institutional memory is not created by consolidation promotion.
| Method | Path | Purpose |
|---|---|---|
POST | /institutional/memories | Create curated institutional memory |
GET | /institutional/memories | List institutional memory |
DELETE | /institutional/memories/{id} | Delete institutional memory |
POST | /institutional/ingest | Ingest one document |
POST | /institutional/ingest/batch | Ingest documents or structured items and edges |
GET | /institutional/documents | List ingested documents and counts |
GET | /institutional/documents/{id}/tree | Get a document with sections and chunks |
DELETE | /institutional/sources/{source} | Forget one populator source |
POST | /agent-memories | Create agent-scoped curated memory |
GET | /agent-memories | List agent-scoped memory |
DELETE | /agent-memories/{id} | Delete agent-scoped memory |
The /agent-memories routes are available in the Core edition.
POST /institutional/ingest/batch accepts documents[], items[], edges[]
and a present manifest in one request, and applies them in that order. A
request carries at most 100 documents or 5000 items.
Items and relations are keyed by about, making repeated population idempotent.
An optional present manifest allows a populator to forget items removed from
its source. Ingest returns 202; embeddings are generated asynchronously.
The schema endpoint is the source of truth for memory vocabulary. A policy may
declare any number of types and relations, including the allowed tiers and
source/target type constraints. Configured schemas are enforced on direct
memory writes, updates, supersedes, batch ingestion, and relation creation.
Configured schemas are not applied to compaction summaries: the compaction
worker writes them through the store (SaveCompactionSummary) without a schema
check. Without a configured schema, writes through POST /memories, PATCH /memories/{id} and POST /agent-memories are validated against the built-in
schema (document, section, chunk, general, preference, fact,
observation, temporal_summary). POST /institutional/memories and batch
ingestion are not constrained.
Operations and administration
Section titled “Operations and administration”The /admin routes are served at the root, not under /api/v1.
| Method | Path | Purpose |
|---|---|---|
POST | /admin/embedding-dimension-change | Record one-shot consent for a dimension change |
GET | /admin/embedding-status | Read current/configured embedding and backlog state |
POST | /admin/reembed | Embed one or more pending batches |
POST | /admin/reembed/calibrate | Measure a re-embed concurrency to set on the Workspace |
GET | /api/v1/memories/projection | Read the Memory Galaxy projection (Enterprise) |
POST | /api/v1/memories/consent-events | Apply a user’s consent revocation per the MemoryPolicy consentRevocation.action (Enterprise; called by privacy-api) |
GET | /api/v1/audit/memories | Query the Enterprise memory audit log |
POST /admin/reembed accepts max_batches (default 1, maximum 200) and
returns embedded, batches, and remaining. Each call embeds up to
max_batches batches and stops early when a batch embeds nothing, so embedded
is 0 when no observation awaits an embedding. remaining is omitted when the
memory-api does not know its workspace. GET /admin/embedding-status returns
the current and configured embedding and the backlog state.
Embedding dimension limits
Section titled “Embedding dimension limits”- The maximum embedding dimension is 4000. memory-api refuses to start with a larger one, and
POST /admin/embedding-dimension-changerejects atarget_dimoutside 1-4000. The Provider CRD refusesspec.embedding.dimensionsabove 4000. - Dimensions up to 2000 use the pgvector
vectortype. Dimensions above 2000 usehalfvec, with a matching HNSW index. - A fresh store with no embeddings is reshaped to the configured dimension without consent. Consent applies only when a column holds embeddings at another dimension.
- Consent is a single row in
memory_embedding_dim_change_consent. memory-api deletes it when the reshape runs, and also on any startup that needs no reshape. GET /admin/embedding-statusreturnsstate(ready,restart_requiredorreembedding),current_dimension,current_type,configured_dimension,pending_target_dimensionandreembed_backlog.