Skip to content

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.

PortPurpose
8080REST API
8081Health and readiness probes
9090Prometheus 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.

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.

RequestResult
Names the verified userServed
Names a different user from the verified one403
Names a user, carries no proof, and the caller is trusted only by namespace403
Names a user and the caller is an exact-listed system serviceServed
Names no userNot checked
Internal service authentication is disabledServed 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:

ParameterMeaning
workspaceWorkspace UID. The tenant check also accepts the name, but a route that matches rows by it returns 500 on a name.
virtual_user_idPseudonymised end-user identity
agentAgent scope
include_sharedGET /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.

MethodPathPurpose
POST/memoriesCreate or update a memory
GET/memoriesList memories in scope
GET/memories/searchSearch memories
POST/memories/retrieveMulti-tier hybrid retrieval
POST/memories/retrieve/semanticWorkspace 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/memoriesDelete all memories for a subject (DSAR)
DELETE/memories/batchDelete up to limit memories for a subject (paginated DSAR)
GET/memories/exportExport memories for a subject
GET/memories/aggregateWorkspace memory aggregates (Enterprise)
GET/memories/conflictsList entities with more than one active observation
GET/memories/{id}/lineageRead consolidation lineage (Enterprise)
POST/relationsAdd a relation between memories
GET/memory/schemaDiscover configured memory types and relation definitions
GET/memory/tool-guidanceThe 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 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.

MethodPathPurpose
POST/institutional/memoriesCreate curated institutional memory
GET/institutional/memoriesList institutional memory
DELETE/institutional/memories/{id}Delete institutional memory
POST/institutional/ingestIngest one document
POST/institutional/ingest/batchIngest documents or structured items and edges
GET/institutional/documentsList ingested documents and counts
GET/institutional/documents/{id}/treeGet a document with sections and chunks
DELETE/institutional/sources/{source}Forget one populator source
POST/agent-memoriesCreate agent-scoped curated memory
GET/agent-memoriesList 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.

The /admin routes are served at the root, not under /api/v1.

MethodPathPurpose
POST/admin/embedding-dimension-changeRecord one-shot consent for a dimension change
GET/admin/embedding-statusRead current/configured embedding and backlog state
POST/admin/reembedEmbed one or more pending batches
POST/admin/reembed/calibrateMeasure a re-embed concurrency to set on the Workspace
GET/api/v1/memories/projectionRead the Memory Galaxy projection (Enterprise)
POST/api/v1/memories/consent-eventsApply a user’s consent revocation per the MemoryPolicy consentRevocation.action (Enterprise; called by privacy-api)
GET/api/v1/audit/memoriesQuery 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.

  • The maximum embedding dimension is 4000. memory-api refuses to start with a larger one, and POST /admin/embedding-dimension-change rejects a target_dim outside 1-4000. The Provider CRD refuses spec.embedding.dimensions above 4000.
  • Dimensions up to 2000 use the pgvector vector type. Dimensions above 2000 use halfvec, 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-status returns state (ready, restart_required or reembedding), current_dimension, current_type, configured_dimension, pending_target_dimension and reembed_backlog.