Appearance
Memory API
Base path: /api (Sanctum bearer + X-Organization-Id or token ability organization:{uuid}).
Ingest
POST /api/memories
Body: content, optional metadata.
Content safety (OB-045)
When OPENBRAIN_CONTENT_SCAN_ENABLED=true (default in production), ingest, upsert, update, and batch items are scanned for high-risk patterns (API keys, payment card-like sequences, email addresses). Rejected content returns 422:
json
{ "error": "...", "code": "content_rejected" }Disable in local/test with OPENBRAIN_CONTENT_SCAN_ENABLED=false.
outcome | HTTP | Meaning |
|---|---|---|
created | 201 | New row stored |
deduplicated | 200 | Exact content_hash replay; metadata unchanged after shallow merge |
merged_metadata | 200 | Exact or semantic near-duplicate; incoming metadata keys merged (dedupe: exact or semantic) |
Keyed upsert (OB-019)
PUT /api/memories
Idempotent sync by external key.
| Field | Required | Description |
|---|---|---|
externalKey | yes | Stable id from an external system |
content | yes | Memory body (NFC-normalized server-side) |
metadata | no | JSON metadata merged into the row |
scope | no | source, project — part of uniqueness with externalKey |
outcome | HTTP | Meaning |
|---|---|---|
created | 201 | New row for this key scope |
deduplicated | 200 | Same content replay; metadata merged |
updated | 200 | Content changed; re-embedded |
Errors: 404 if the key matches a soft-deleted row; 409 if content_hash belongs to another row.
Semantic near-duplicate merge is not applied on this path.
Batch ingest (OB-020)
POST /api/memories/batch
| Field | Required | Description |
|---|---|---|
items | yes | Array of { content, metadata?, externalKey?, scope? } |
continueOnError | no | Default true; stop after first failure when false |
Response 200: results (per-row success fields + index, or { index, error: { code, message } }) and summary: { ok, failed }.
413 when items.length exceeds OPENBRAIN_BATCH_INGEST_MAX (default 50).
Items with externalKey use keyed upsert; others use standard ingest (including dedupe).
Update
PATCH /api/memories/{id} ( PUT alias for backward compatibility)
Requires memory:admin.
| Field | Required | Description |
|---|---|---|
content | no* | Replaces body; re-embeds when changed |
metadata | no* | Merged into existing metadata by default |
metadataReplace | no | When true, replaces metadata instead of merging |
* At least one of content or metadata is required.
Response 200 with outcome: updated. 409 when new content hashes to another row’s content_hash.
Delete
DELETE /api/memories/{id}
Requires memory:admin. Soft-deletes the row. Response 204 with an empty body.
Export (OB-035)
GET /api/memories/export
Requires memory:search. Returns NDJSON (application/x-ndjson) with one memory per line: id, content, metadata, createdAt, updatedAt, optional deletedAt (no embedding vectors).
Query: limit (default 100, max 500), cursor (from X-Next-Cursor on the previous page), optional filter, includeDeleted.
Rows are ordered by created_at ascending for stable pagination.
Stats (OB-034)
GET /api/memories/stats
Requires memory:search. Returns facet counts for metadata keys agentId, source, surface, project, kind, plus meta.total and meta.filtered.
Changes / delta (OB-064)
GET /api/memories/changes
Requires memory:search. Returns memories created, updated, or (with includeDeleted) soft-deleted since an ISO-8601 since timestamp.
Query: since (required), limit (default 100, max 500), cursor (from nextCursor in the prior response), optional filter, includeDeleted.
Response 200:
json
{
"changes": [
{ "id": "...", "content": "...", "metadata": {}, "change": "created", "createdAt": "...", "updatedAt": "..." },
{ "id": "...", "deletedAt": "...", "change": "deleted" }
],
"meta": { "count": 2 },
"nextCursor": "2026-05-22T12:00:00+00:00|uuid"
}Active rows use the same fields as single-record reads (no embeddings). Deleted rows omit content.
Large documents (OB-044)
Chunk large inputs across multiple ingest or batch rows using metadata:
| Key | Type | Description |
|---|---|---|
documentId | string | Stable id for the document |
chunkIndex | int | Zero-based index |
chunkTotal | int | Total chunks |
If any chunk field is set, all three are required and 0 <= chunkIndex < chunkTotal.
Read concatenated chunks via MCP resource brain://{organizationId}/document/{documentId}.
Recent list (OB-032)
GET /api/memories/recent
Requires memory:search. Query: limit (default 20, max 50), optional filter (metadata key/value), includeDeleted.
Response 200: { "results": [...], "meta": { "count": N } } — newest created_at first.
MCP
| Tool | REST equivalent |
|---|---|
memory_ingest | POST /api/memories |
memory_upsert | PUT /api/memories |
memory_ingest_batch | POST /api/memories/batch |
memory_search | POST /api/memories/search |
memory_stats | GET /api/memories/stats |
memory_changes | GET /api/memories/changes |
memory_recent | GET /api/memories/recent |
memory_get | GET /api/memories/{id} |
memory_update | PATCH /api/memories/{id} |
memory_delete | DELETE /api/memories/{id} |
openbrain_health | readiness checks (MCP) — see health.md |