Skip to main content

Memory

Store and retrieve memories in the Memory Library. Memories can belong to one or more agents (linkedIds), are kind-typed (fact / episodic / semantic / instruction / preference), versioned, and bitemporal (validFrom / validUntil). Search blends BM25 + dense vectors via RRF with temporal decay; ingest runs a Mem0-style LLM judge that decides ADD / UPDATE / DELETE / NOOP against retrieved candidates.

All endpoints require authentication via X-API-Key header and the appropriate scope.


Memory Object​

{
"_id": "mem_abc123",
"ownerId": "user_456",
"ownerName": "Alice Example",
"organizationId": "org_789",
"sharedWith": [],
"isPublic": false,
"source": "user",
"tags": ["preference", "languages"],
"category": "engineering",
"linkedIds": [],
"currentVersion": 2,
"kind": "fact",
"content": "User prefers TypeScript over JavaScript for new projects.",
"summary": "Prefers TypeScript",
"eventTime": "2026-05-01T09:00:00Z",
"validFrom": "2026-05-01T09:00:00Z",
"validUntil": null,
"sourceThreadId": "thr_789",
"sourceMessageId": "msg_456",
"sourceToolName": null,
"importance": 0.7,
"decayHalfLifeDays": 90,
"usageCount": 4,
"lastAccessedAt": "2026-05-10T12:00:00Z",
"links": [
{ "targetId": "mem_def456", "relation": "supports", "weight": 0.8 }
],
"confidence": 0.9,
"contradictionOf": null,
"scoreResult": {
"score": 82,
"grade": "B",
"rulesetVersion": "1.0.0",
"failedCriticalCount": 0,
"failedHighCount": 1
},
"lastAssessedAt": "2026-05-10T12:00:00Z",
"createdAt": "2026-05-01T09:00:00Z",
"updatedAt": "2026-05-10T12:00:00Z"
}

linkedIds lists the agents the memory belongs to. scoreResult (its quality score and grade) and lastAssessedAt are kept current by every change to the memory; assess returns the full report behind them.


GET /api/v1/primitives/memories​

List memories with pagination, kind/tag/search filters, a linked-agent filter, and bitemporal asOf selection.

Scope: memory:read

Query Parameters

ParameterTypeRequiredDescription
kindstringNoFilter by kind: fact, episodic, semantic, instruction, preference
tagsstringNoComma-separated tag list; a memory must carry all of them
linkedIdsstringNoComma-separated agent IDs; a memory must belong to all of them
qstringNoFull-text search on content, summary, and tags
limitintegerNoMax results (default 50, max 200)
cursorstringNometa.nextCursor of the previous page; omit for the first page
asOfstringNoISO timestamp -- return only memories valid at this instant (bitemporal). Defaults to now; 400 validation-error if not a valid date
includeInvalidatedbooleanNotrue includes rows whose validUntil is in the past (and asOf is then ignored)

Response 200 OK

{
"data": [ /* Memory objects */ ],
"meta": {
"total": 25,
"limit": 50,
"nextCursor": null,
"requestId": "req_abc123"
}
}

POST /api/v1/primitives/memories​

Create a new memory.

Scope: memory:write

Request Body

{
"kind": "fact",
"content": "User prefers TypeScript over JavaScript for new projects.",
"summary": "Prefers TypeScript",
"tags": ["preference", "languages"],
"category": "engineering",
"source": "user",
"linkedIds": [],
"importance": 0.7,
"confidence": 0.9,
"decayHalfLifeDays": 90,
"eventTime": "2026-05-01T09:00:00Z",
"validFrom": "2026-05-01T09:00:00Z",
"validUntil": null,
"sourceThreadId": "thr_789",
"sourceMessageId": "msg_456",
"sourceToolName": null,
"links": []
}
FieldTypeRequiredDescription
kindstringYesfact / episodic / semantic / instruction / preference
contentstringYesMemory body
summarystringNoOne-line summary
tagsstring[]NoDiscoverability tags
categorystringNoFree-form category
sourcestringNoProvenance label (default: user)
linkedIdsstring[]NoAgent IDs the memory belongs to. When set, the memory is owned by that agent's owner
importancenumberNo0..1 retention weight
confidencenumberNo0..1 truth confidence
decayHalfLifeDaysnumberNoPer-row temporal decay half-life used at search time
eventTimestringNoISO timestamp the memory describes
validFromstringNoISO timestamp the assertion starts being true
validUntilstring | nullNoISO timestamp the assertion stops being true (null = open-ended)
sourceThreadIdstringNoOriginating thread
sourceMessageIdstringNoOriginating message
sourceToolNamestringNoOriginating tool call
linksobject[]NoInitial graph edges to other memories

Response 201 Created

{
"data": { "memoryId": "mem_abc123" },
"meta": { "requestId": "req_abc123" }
}

GET /api/v1/primitives/memories/:id​

Get a single memory by ID.

Scope: memory:read

Path Parameters

ParameterTypeRequiredDescription
idstringYesMemory ID

Response 200 OK

Returns the full Memory object.


PATCH /api/v1/primitives/memories/:id​

Update a memory. Content changes append a new version row -- history is preserved.

Scope: memory:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesMemory ID

Request Body

FieldTypeRequiredDescription
kindstringNoMemory kind
contentstringNoNew content (triggers a new version snapshot)
summarystringNoOne-line summary
tagsstring[]NoTags
categorystring | nullNoCategory (pass null to clear)
eventTimestringNoISO timestamp
validFromstringNoISO timestamp
validUntilstring | nullNoISO timestamp or null
sourceThreadIdstringNoThread provenance
sourceMessageIdstringNoMessage provenance
sourceToolNamestringNoTool provenance
importancenumberNo0..1 retention weight
decayHalfLifeDaysnumberNoPer-row decay half-life
confidencenumberNo0..1 truth confidence
linksobject[]NoGraph edges
linkedIdsstring[]NoReplace the agents and pools the memory belongs to
changeNotestringNoFree-form note attached to the new version

Response 200 OK​

The memory, as GET /primitives/memories/:id shows it.


DELETE /api/v1/primitives/memories/:id​

Delete a memory and its version history. Irreversible.

Scope: memory:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesMemory ID

Response 204 No Content


POST /api/v1/primitives/memories/:id/access-events​

Record an access -- increments usageCount and updates lastAccessedAt.

Scope: memory:read

Path Parameters

ParameterTypeRequiredDescription
idstringYesMemory ID

Response 201 Created: { "usageCount": 5 }, how many times the memory has been used.


Sharing​

Who can reach a memory is the platform's one sharing shape: its owner, its members (role editor can use and change it, user can only use it) and its visibility (public: every user can find and use it; in a multi-tenant deployment, every user of its organization). Changing it stays with its owner and editors.

MethodPathDoesScope
GET/api/v1/primitives/memories/:id/permissionsOwner, members (userId, role) and visibilitymemory:read
POST/api/v1/primitives/memories/:id/permissions/membersShare with a user: { "userId", "role": "editor" | "user" }memory:write
DELETE/api/v1/primitives/memories/:memoryId/permissions/members/:userIdStop sharing with a usermemory:write
PATCH/api/v1/primitives/memories/:id/permissions{ "visibility": "public" | "private" }memory:write

Each, except a member's removal (204), answers the permissions as they are now:

{
"data": {
"resourceId": "<memory id>",
"owner": "<user id>",
"members": [{ "userId": "<user id>", "role": "user" }],
"visibility": "private"
},
"meta": { "requestId": "req_abc123" }
}

POST /api/v1/primitives/memories/import​

Bulk re-ingest of the JSON shape GET /api/v1/primitives/memories/export emits.

Scope: memory:write

Request Body

{
"memories": [ /* Memory rows, or pass the full export wrapper */ ]
}
FieldTypeRequiredDescription
memoriesarrayNoArray of memory rows. If absent, the entire body is treated as the export wrapper.

Response 200 OK

{
"data": { "imported": 41, "unchanged": 1, "skipped": 1, "errors": [{ "index": 7, "message": "row.content must be a non-empty string" }] },
"meta": { "requestId": "req_abc123" }
}

Imported memories belong to the caller. imported counts new memories; unchanged counts rows the caller already has (same content), which are not created again; skipped counts refused rows, each in errors with its index (from 0).


GET /api/v1/primitives/memories/export​

Bulk-export every memory the caller can read as JSON.

Scope: memory:read

Query Parameters

ParameterTypeRequiredDescription
includeInvalidatedbooleanNoInclude rows with validUntil in the past

Response 200 OK

{
"data": {
"exportedAt": "2026-05-16T10:00:00Z",
"count": 25,
"memories": [ /* Memory objects */ ]
},
"meta": { "requestId": "req_abc123" }
}

POST /api/v1/primitives/memories/consolidate​

Run deterministic sleep-time consolidation: detects duplicates, evicts stale low-value rows, promotes heavily-used ones. Pass dryRun: true to preview without writing.

Scope: memory:write

Request Body

{
"config": {
"evictionScoreThreshold": 0.2,
"evictionIdleDays": 90,
"duplicateOverlapThreshold": 0.85,
"promotionUsageThreshold": 10,
"promotionImportanceCeiling": 0.5,
"promotionImportanceTarget": 0.8
},
"dryRun": false
}
FieldTypeRequiredDescription
configobjectNoOverride default thresholds
dryRunbooleanNoReturn the plan without applying changes

Response 200 OK

{
"data": {
"ownerId": "user_456",
"config": { /* thresholds used for this run */ },
"candidatesScanned": 120,
"plan": {
"evictions": [ /* memories selected for eviction */ ],
"promotions": [ /* memories selected for promotion */ ],
"duplicates": [ /* duplicate clusters */ ]
},
"applied": true,
"duplicatesInvalidated": 2,
"evictionsInvalidated": 3,
"promotionsApplied": 1,
"memoriesAutoLinked": 4,
"autoLinksAdded": 6,
"decayBackfilled": 0,
"startedAt": "2026-05-16T10:00:00Z",
"finishedAt": "2026-05-16T10:00:02Z"
},
"meta": { "requestId": "req_abc123" }
}

With dryRun: true, applied is false, plan lists what would change, and the three applied counts are 0.


POST /api/v1/primitives/memories/:id/assess​

Run the Memory quality ruleset against a row and cache the score on it. Returns the full per-check report.

Scope: memory:read

Path Parameters

ParameterTypeRequiredDescription
idstringYesMemory ID

Response 200 OK

{
"data": {
"rulesetVersion": "1.0.0",
"assessedAt": "2026-05-16T10:00:00Z",
"score": 82,
"grade": "B",
"weightAwarded": 41,
"weightTotal": 50,
"failedCriticalCount": 0,
"failedHighCount": 1,
"checks": [
{
"id": "mem-content-nonempty",
"title": "Content is non-empty",
"category": "content",
"severity": "critical",
"weight": 10,
"awarded": 10,
"status": "pass",
"passed": true
}
]
},
"meta": { "requestId": "req_abc123" }
}

Each check's status is pass, fail or skipped. A skipped check does not apply to this row (for example the eventTime check on a memory that is not episodic): it earns nothing and its weight is left out of weightTotal, so score is weightAwarded / weightTotal over the checks that apply.


POST /api/v1/primitives/memories/ingest​

Mem0-style ingest. Embeds the new content, retrieves similar memories, asks an LLM to decide ADD / UPDATE / DELETE / NOOP, and executes the action.

Scope: memory:write

Request Body

{
"content": "User prefers TypeScript over JavaScript for new projects.",
"kind": "preference",
"tags": ["preference", "languages"],
"linkedIds": [],
"model": "gpt-4o",
"candidateK": 5
}
FieldTypeRequiredDescription
contentstringYesThe new piece of information
kindstringYesfact / episodic / semantic / instruction / preference
tagsstring[]NoTags applied if the judge chooses ADD
linkedIdsstring[]NoAgent IDs: the judge compares against those agents' memories, and the resulting memory belongs to them
modelstringNoOverride the judge LLM model
candidateKnumberNoHow many similar memories to show the LLM (default 5)

Response 200 OK

{
"data": {
"action": "ADD",
"memoryId": "mem_abc123",
"reasoning": "No existing memory covers this preference."
},
"meta": { "requestId": "req_abc123" }
}

action is ADD, UPDATE, DELETE or NOOP. memoryId is omitted for NOOP.

Returns 400 validation-error for empty content/kind or judge parse errors; 503 configuration-error if no judge model or org is configured.


POST /api/v1/primitives/memories/:id/links​

Create or update a graph edge from this memory to another. The edge is stored on this memory only (you need edit access to it and read access to the target); the target lists it as an incoming link (GET /primitives/memories/:id/backlinks).

Scope: memory:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesSource memory ID

Request Body

FieldTypeRequiredDescription
targetIdstringYesTarget memory ID
relationstringYessupports / contradicts / refines / related
weightnumberNoEdge weight in [0, 1]

Response 200 OK

{
"data": { "success": true },
"meta": { "requestId": "req_abc123" }
}

Remove the link from this memory to a target. Only this memory changes; a link the target has back to it stays.

Scope: memory:write

Path Parameters

ParameterTypeRequiredDescription
memoryIdstringYesSource memory ID
idstringYesTarget memory ID

Response 200 OK

{
"data": { "success": true },
"meta": { "requestId": "req_abc123" }
}

List memories that link to this one (reverse-edge lookup).

Scope: memory:read

Path Parameters

ParameterTypeRequiredDescription
idstringYesMemory ID

Response 200 OK

{
"data": {
"memories": [ /* Memory objects with edges pointing to :id */ ],
"count": 3
},
"meta": { "requestId": "req_abc123" }
}

POST /api/v1/primitives/memories/:id/invalidate​

Mark a memory as contradicted or superseded. Sets validUntil = now and contradictionOf (if supplied). History is preserved.

Scope: memory:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesMemory ID

Request Body

FieldTypeRequiredDescription
supersededBystringNoMemory ID that supersedes this one

Response 200 OK​

The memory, as GET /primitives/memories/:id shows it, its validUntil set.


POST /api/v1/primitives/memories/search​

Hybrid (BM25 + dense vector) memory search with RRF fusion and temporal-decay re-ranking. Kind, tag, and linked-agent filters apply uniformly to both signals.

Scope: memory:read

Request Body

{
"query": "What does the user prefer for new TypeScript projects?",
"k": 10,
"kind": "preference",
"tags": ["languages"],
"rrfK": 60,
"weights": { "vector": 1.0, "text": 1.0 },
"decayHalfLifeDaysOverride": 90,
"asOf": "2026-05-16T10:00:00Z",
"includeInvalidated": false,
"rerank": true,
"rerankModel": "bge-reranker"
}
FieldTypeRequiredDescription
querystringYesNatural-language query
knumberNoMax results (default 10, max 200)
kindstringNoFilter by memory kind
tagsstring[]NoFilter by tags; a memory must carry all of them
linkedIdsstring[]NoAgent IDs; a memory belonging to any of them matches, and memories shared with the whole organization are included too
rrfKnumberNoRRF k constant (default 60)
weightsobjectNo{vector?, text?} per-list weights for RRF
decayHalfLifeDaysOverridenumber | nullNoOverride per-row decay; pass null to disable decay
asOfstringNoISO timestamp for bitemporal filter
includeInvalidatedbooleanNoInclude invalidated rows
rerankbooleanNoRun a reranker over the fused list
rerankModelstringNoOverride the reranker model

Response 200 OK

{
"data": {
"results": [
{ "memory": { /* Memory object */ }, "score": 0.0312, "rank": 1 }
],
"count": 1,
"contextBlock": "## Relevant memories\n..."
},
"meta": { "requestId": "req_abc123" }
}

results are ordered best first (rank 1 is the best match). contextBlock is the same results rendered as a markdown block ready to place in a system prompt.


GET /api/v1/primitives/memories/:id/versions​

List every version of a memory with reconstructed content, newest first.

Scope: memory:read

Path Parameters

ParameterTypeRequiredDescription
idstringYesMemory ID

Response 200 OK

{
"data": {
"versions": [
{
"versionNumber": 2,
"content": "Updated content",
"changeNote": "Clarified phrasing",
"createdAt": "2026-05-10T12:00:00Z"
},
{
"versionNumber": 1,
"content": "Original content",
"changeNote": null,
"createdAt": "2026-04-01T09:00:00Z"
}
],
"count": 2
},
"meta": { "requestId": "req_abc123" }
}

GET /api/v1/primitives/memories/:memoryId/versions/:version​

Fetch one version of a memory with reconstructed content.

Scope: memory:read

Path Parameters

ParameterTypeRequiredDescription
memoryIdstringYesMemory ID
versionintegerYesVersion number (positive integer)

Response 200 OK

{
"data": {
"versionNumber": 2,
"content": "Updated content",
"changeNote": "Clarified phrasing",
"createdAt": "2026-05-10T12:00:00Z"
},
"meta": { "requestId": "req_abc123" }
}

POST /api/v1/primitives/memories/:memoryId/versions/:version/restore​

Restore the memory to a prior version. Appends a new version row -- does not mutate history.

Scope: memory:write

Path Parameters

ParameterTypeRequiredDescription
memoryIdstringYesMemory ID
versionintegerYesVersion number to restore to

Response 200 OK​

The memory, as GET /primitives/memories/:id shows it: its content is the restored version's, saved as a new version.


POST /api/v1/primitives/memories/:id/promote​

Put a memory in your organization's org-global pool, so every agent in the organization reads it. Use it for a durable fact the whole team should share.

Scope: memory:write

ParameterTypeRequiredDescription
idstringYesMemory id

Response 200 OK: the memory, its linkedIds now holding the org-global pool's id.


POST /api/v1/primitives/memories/:id/demote​

Take a memory out of the org-global pool: agents stop reading it from there.

Scope: memory:write

ParameterTypeRequiredDescription
idstringYesMemory id

Response 200 OK: the memory.