Skip to main content

Memory

Store and retrieve memories in the Memory Library. Memories are scoped (user-global, agent, app, or workflow), 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",
"kind": "fact",
"content": "User prefers TypeScript over JavaScript for new projects.",
"summary": "Prefers TypeScript",
"tags": ["preference", "languages"],
"category": "engineering",
"source": "chat",
"scope": {
"agentId": null,
"appId": null,
"workflowId": null
},
"importance": 0.7,
"confidence": 0.9,
"decayHalfLifeDays": 90,
"usageCount": 4,
"lastAccessedAt": "2026-05-10T12:00:00Z",
"eventTime": "2026-05-01T09:00:00Z",
"validFrom": "2026-05-01T09:00:00Z",
"validUntil": null,
"sourceThreadId": "thr_789",
"sourceMessageId": "msg_456",
"sourceToolName": null,
"links": [
{ "targetId": "mem_def456", "relation": "supports", "weight": 0.8 }
],
"contradictionOf": null,
"qualityScore": 0.82,
"isPublic": false,
"ownerId": "user_456",
"createdAt": "2026-05-01T09:00:00Z",
"updatedAt": "2026-05-10T12:00:00Z"
}

GET /api/v1/memory

List memories with pagination, kind/tag/search filters, scope filters, and bitemporal asOf selection.

Scope: memory:read

Query Parameters

ParameterTypeRequiredDescription
kindstringNoFilter by kind: fact, episodic, semantic, instruction, preference
tagsstringNoComma-separated tag list (matches any)
searchstringNoFull-text search on content, summary, and tags
limitintegerNoMax results (default 50, max 200)
offsetintegerNoPagination offset
agentIdstringNoNarrow to memories scoped to this agent
appIdstringNoNarrow to memories scoped to this app instance
workflowIdstringNoNarrow to memories scoped to this workflow
asOfstringNoISO timestamp — return only memories valid at this instant (bitemporal)
includeInvalidatedbooleanNoInclude rows whose validUntil is in the past

Response 200 OK

{
"count": 25,
"limit": 50,
"offset": 0,
"memories": [ /* Memory objects */ ]
}

POST /api/v1/memory

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": "chat",
"scope": { "agentId": null, "appId": null, "workflowId": null },
"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 (e.g. chat, import, agent)
scopeobjectNo{agentId?, appId?, workflowId?}; absence = user-global. Top-level agentId / appId / workflowId are also accepted.
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

{ "memoryId": "mem_abc123" }

GET /api/v1/memory/:id

Get a single memory by ID.

Scope: memory:read

Path Parameters

ParameterTypeRequiredDescription
idstringYesMemory ID

Response 200 OK

Returns the full Memory object.


PUT /api/v1/memory/: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
changeNotestringNoFree-form note attached to the new version

Response 200 OK

Returns the updated Memory object.


DELETE /api/v1/memory/:id

Delete a memory and its version history. Irreversible.

Scope: memory:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesMemory ID

Response 204 No Content


POST /api/v1/memory/:id/access

Record an access — increments usageCount and updates lastAccessedAt.

Scope: memory:read

Path Parameters

ParameterTypeRequiredDescription
idstringYesMemory ID

Response 200 OK

{ "recorded": true }

POST /api/v1/memory/:id/share

Share a memory with another user.

Scope: memory:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesMemory ID

Request Body

FieldTypeRequiredDescription
userIdstringYesUser to share with

Response 200 OK

{ "success": true }

POST /api/v1/memory/:id/unshare

Remove sharing with a user.

Scope: memory:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesMemory ID

Request Body

FieldTypeRequiredDescription
userIdstringYesUser to unshare from

Response 200 OK

{ "success": true }

POST /api/v1/memory/import

Bulk re-ingest of the JSON shape /memory/export emits.

Scope: memory:write

Request Body

{
"memories": [ /* Memory rows, or pass the full export wrapper */ ],
"scope": { "agentId": null, "appId": null, "workflowId": null }
}
FieldTypeRequiredDescription
memoriesarrayNoArray of memory rows. If absent, the entire body is treated as the export wrapper.
scopeobjectNoOverride scope for all imported rows

Response 200 OK

{ "imported": 42, "skipped": 1, "errors": [] }

GET /api/v1/memory/export

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

Scope: memory:read

Query Parameters

ParameterTypeRequiredDescription
includeInvalidatedbooleanNoInclude rows with validUntil in the past
agentIdstringNoNarrow to scope
appIdstringNoNarrow to scope
workflowIdstringNoNarrow to scope

Response 200 OK

{
"memories": [ /* Memory objects */ ],
"exportedAt": "2026-05-16T10:00:00Z"
}

POST /api/v1/memory/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

{
"scope": { "agentId": null, "appId": null, "workflowId": null },
"config": {
"evictionScoreThreshold": 0.2,
"evictionIdleDays": 90,
"duplicateOverlapThreshold": 0.85,
"promotionUsageThreshold": 10,
"promotionImportanceCeiling": 0.5,
"promotionImportanceTarget": 0.8
},
"dryRun": false
}
FieldTypeRequiredDescription
scopeobjectNoNarrow consolidation to a scope
configobjectNoOverride default thresholds
dryRunbooleanNoReturn the plan without applying changes

Response 200 OK

{
"evicted": 3,
"promoted": 1,
"deduplicated": 2,
"dryRun": false
}

POST /api/v1/memory/: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

{
"score": 0.82,
"checks": [
{ "id": "has-summary", "passed": true, "weight": 0.2 },
{ "id": "has-tags", "passed": true, "weight": 0.1 }
]
}

POST /api/v1/memory/:id/toggle-public

Toggle whether a memory is readable across the organization.

Scope: memory:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesMemory ID

Response 200 OK

{ "isPublic": true }

PATCH /api/v1/memory/:id/scope

Re-scope an existing memory. Owner-only — the row owner can move a memory between user-global / agent / app / workflow scope without rewriting any content or losing version history.

Per the per-agent isolation model: a memory created without an explicit scope by a marketplace-app instance defaults to that app's scope (see POST /api/v1/memory); this endpoint lets the owner change that later. The request body shape mirrors create: either nested scope: {...} or flat top-level {agentId, appId, workflowId} — empty / whitespace values collapse to null. Sending {"scope": {}} is the explicit opt-out to user-global.

Scope: memory:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesMemory ID

Request Body (either form accepted)

{
"scope": {
"agentId": "agent-123",
"appId": null,
"workflowId": null
}
}

or flat:

{
"agentId": "agent-123"
}

Response 200 OK

{
"success": true,
"scope": {
"agentId": "agent-123",
"appId": null,
"workflowId": null
}
}

Status Codes

CodeMeaning
200Scope updated
400Invalid memory ID
401Missing / invalid auth
403Caller is not the row owner
404Memory not found (or hidden from caller via per-agent isolation)

POST /api/v1/memory/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"],
"scope": { "agentId": null, "appId": null, "workflowId": null },
"model": "gpt-4o",
"candidateK": 5
}
FieldTypeRequiredDescription
contentstringYesThe new piece of information
kindstringYesfact / episodic / semantic / instruction / preference
tagsstring[]NoTags applied if the judge chooses ADD
scopeobjectNo{agentId?, appId?, workflowId?}
modelstringNoOverride the judge LLM model
candidateKnumberNoHow many similar memories to show the LLM (default 5)

Response 200 OK

{
"action": "ADD",
"memoryId": "mem_abc123",
"candidates": [ /* candidates the judge saw */ ]
}

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/memory/:id/links

Create or update a graph edge from this memory to another. Symmetric relations (supports / contradicts / related) are mirrored on the target.

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

{ "success": true }

Remove the link from this memory to a target. Symmetric relations are also stripped from the target side.

Scope: memory:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesSource memory ID
targetIdstringYesTarget memory ID

Response 200 OK

{ "success": true }

GET /api/v1/memory/:id/linked-to

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

Scope: memory:read

Path Parameters

ParameterTypeRequiredDescription
idstringYesMemory ID

Response 200 OK

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

POST /api/v1/memory/: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

{
"memoryId": "mem_abc123",
"validUntil": "2026-05-16T10:00:00Z",
"supersededBy": "mem_def456"
}

POST /api/v1/memory/search

Hybrid (BM25 + dense vector) memory search with RRF fusion and temporal-decay re-ranking. Scope, kind, and tag 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"],
"scope": { "agentId": null, "appId": null, "workflowId": null },
"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 (OR)
scopeobjectNo{agentId?, appId?, workflowId?}
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

{
"results": [
{ "memory": { /* Memory object */ }, "score": 0.91, "signals": { "vector": 0.93, "text": 0.78, "decay": 0.95 } }
],
"count": 1
}

GET /api/v1/memory/:id/versions

List every version of a memory with reconstructed content.

Scope: memory:read

Path Parameters

ParameterTypeRequiredDescription
idstringYesMemory ID

Response 200 OK

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

GET /api/v1/memory/:id/versions/:n

Fetch one version of a memory with reconstructed content.

Scope: memory:read

Path Parameters

ParameterTypeRequiredDescription
idstringYesMemory ID
nintegerYesVersion number (positive integer)

Response 200 OK

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

POST /api/v1/memory/:id/versions/:n/restore

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

Scope: memory:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesMemory ID
nintegerYesVersion number to restore to

Response 200 OK

{
"memoryId": "mem_abc123",
"restoredFrom": 1,
"newVersionNumber": 3
}