Core Concepts
Memory Fundamentals
A memory in MemorySync is a single durable record of one piece of knowledge — a preference, a fact, an event, a decision, an insight — that the API can recall by meaning later. This page is the mental model: what a memory is, how it gets created, and what scopes own it.
A one-line definition you can quote
A memory is a row in the memories table that pairs an encrypted text payload with an embedding vector, a set of scope keys (tenant, project, user, environment), a set of scoring signals, and a lifecycle state.
Why "encrypted"
Each user has a per-user encryption key. The
text column stores authenticated-encryption ciphertext, not plain text. Only the API process can decrypt it during retrieval.The two ways memories come into existence
| Path | Trigger | What you supply |
|---|---|---|
| Direct write | POST /memory/add | A text string (or its alias content) plus optional metadata. |
| Extraction | Integration sync, web crawl, or chat capture | Raw external content. The extraction pipeline produces one or more MemoryCandidate rows that are promoted into Memory rows after validation. |
Both paths end the same way: a row in memories with an embedding, a tier, and a retention status.
The five scope keys every memory carries
| Key | Source | Why it exists |
|---|---|---|
user_id | Server-resolved (JWT user, or end-user mapped from X-End-User-ID). | Hard owner of the memory. Body user_id is ignored. |
tenant_id | Read off the user's row, never trusted from input. | Outer isolation boundary — every read must filter on it. |
project_id | X-Project-ID header, or pinned on the API key. | Sub-scope inside a tenant. Nullable for org-wide memories. |
environment | Stamped from request context — defaults to production. | Must be one of development, staging, production. |
end_user_id (optional) | X-End-User-ID header on service-auth calls. | Lets one API key represent many real users without collision. |
What you write vs what you read back
The request shape is small. The response shape is large because the platform enriches the record on the way in.
| Stage | Field set |
|---|---|
| Request body | text (or content), optional metadata, tags, source, event_type, importance, ttl_minutes, deduplicate, user_id, project_id. |
| Server overrides | user_id, project_id, environment are always taken from request context, never from the body. |
| Computed at write | vector, vector_id, embedding model and version, semantic tags, tier, retention_status, importance_score, quality_score, confidence_score, freshness_score. |
| Response | MemoryResponse with the stored fields plus the semantic enrichment. |
When a memory becomes recallable
- 1The row is committed with
retention_status = "active". - 2The text is embedded into the configured embedding model.
- 3The vector and its scope metadata land in the per-user vector namespace.
- 4
POST /memory/queryreturns the row only ifretention_status = "active"AND the requested scope keys match.
What a memory is not
- Not a chat message — see Sessions & Threads for conversation modeling.
- Not a file or attachment — only text content is stored. Integrations extract memories from files; the file itself is not a memory.
- Not a log line — auditable lifecycle events live in
memory_events, not in the memory row. - Not bound to a session — memories are scoped to
(tenant, project, user), never to a single conversation.
Quick reference card
| Question | Answer |
|---|---|
| What identifies a memory uniquely? | id (integer primary key) plus vector_id (string) for the vector store side. |
| What text is actually embedded? | Semantic-enriched text produced by the extraction or preprocessing step — not the raw input string. |
| Can two memories carry the same content? | Yes, but writes use source_input_hash to detect duplicates; with deduplicate=true (default) the API may skip the insert. |
| Does a memory ever update? | Lifecycle fields update (tier, retention_status, usage_count, decay_score). The text payload itself is immutable. |