memory API
Memory as an API for AI agents. Three verbs — remember, recall, forget.
Getting started
Every memory account gets a private, sovereign store. You interact with it through a tiny HTTP API. No SDK required — just curl or fetch.
1. Register
curl -X POST https://memory.automators.work/v1/register \
-H "Content-Type: application/json" \
-d '{ "email": "you@example.com" }'
Response includes your api_key — shown only once. Save it now.
2. Authenticate
Every authenticated endpoint needs the Authorization header:
Authorization: Bearer ctxk_...
Memory API
POST/v1/remember
Stores a new memory. Content is embedded automatically.
Body
| field | type | notes |
|---|---|---|
content | string | required, max 20,000 chars |
type | string | optional. Allowed: fact, preference, decision, task, correction, event, instruction, note. Default: note. |
auto_classify | boolean | optional. If true and type is not provided, the LLM (Granite 4.0 H Micro) classifies the content into one of the allowed types. Adds ~300-600 ms. |
tags | string[] | optional, used for filtering in recall |
metadata | object | optional, free-form JSON |
supersedes | string | optional. ID of a prior entry this one supersedes. The prior entry is marked superseded_by and hidden from default recall/list. Use /chain to see the full history. |
Idempotency: if an active entry already exists with the same (content, type), the server returns the existing entry with idempotent_hit: true and HTTP 200 instead of creating a duplicate (HTTP 201). Content hashes are 32-char SHA-256 prefixes stored as content_hash.
curl -X POST https://memory.automators.work/v1/remember \
-H "Authorization: Bearer ctxk_..." \
-H "Content-Type: application/json" \
-d '{
"content": "User prefers concise answers in Spanish.",
"type": "preference",
"tags": ["user:alice", "lang:es"]
}'
Response (201 Created)
{
"id": "2026-04-17T14-22-03-8a4f2c",
"content": "User prefers concise answers in Spanish.",
"type": "preference",
"tags": ["user:alice", "lang:es"],
"metadata": {},
"created_at": 1744900923000,
"took_ms": 142
}
POST/v1/remember/from-session
Extracts atomic memories from a free-form transcript using an LLM. Ideal for post-session mining.
Body
| field | type | notes |
|---|---|---|
transcript | string | 20–100,000 chars. Older content is trimmed to keep the recent tail. |
max_memories | number | default 10, max 20 |
curl -X POST https://memory.automators.work/v1/remember/from-session \
-H "Authorization: Bearer ctxk_..." \
-H "Content-Type: application/json" \
-d '{
"transcript": "User: I prefer answers in Spanish.\nAssistant: Got it.\nUser: Also I use Rust daily.",
"max_memories": 5
}'
Response (201 Created)
{
"extracted": 2,
"entries": [
{ "id": "...", "content": "User prefers answers in Spanish", "type": "preference", "tags": ["lang:es"] },
{ "id": "...", "content": "User uses Rust daily", "type": "fact", "tags": ["lang:rust"] }
],
"took_ms": { "llm": 1240, "total": 1890 },
"model": "@cf/ibm-granite/granite-4.0-h-micro"
}
Default model is IBM Granite 4.0 H Micro (131K context, efficient for structured JSON extraction). Each extracted memory is committed individually and auto-embedded.
POST/v1/recall
Hybrid search across two channels fused with Reciprocal Rank Fusion:
- Vector (Matryoshka) — coarse 64d filter → fine 768d rerank (EmbeddingGemma-300m).
- Full-text (BM25-lite) — Unicode-aware tokenization + IDF-weighted term matching over content + tags.
After fusion: correction boost (2× for type:"correction") then MMR diversity (λ=0.7 default). Disable RRF to save a few ms if you only want vector search.
Body
| field | type | notes |
|---|---|---|
query | string | required |
top_k | number | default 10, max 50 |
coarse_top_n | number | default 100, max 500 — candidates after coarse pass |
type | string | filter by type (e.g. fact, preference, correction) |
tags | string[] | ALL tags must match |
since | number | unix ms — only entries created after |
rrf | boolean | default true — fuse vector + full-text channels. Set false for vector-only (faster on large memories). |
mmr | boolean | default true — applies MMR diversity so near-duplicates don't crowd results |
lambda | number | default 0.7 — 1.0 = pure relevance, 0.0 = pure diversity |
correction_boost | number | default 2.0 — score multiplier for entries with type:"correction" |
synthesize | boolean | default false — opt-in LLM call (Granite 4.0 H Micro) that returns an answer field synthesized from the top results. Adds ~500-1500 ms. |
Tip: use type:"correction" when remembering overrides of prior information. They'll surface above older entries at recall time.
curl -X POST https://memory.automators.work/v1/recall \
-H "Authorization: Bearer ctxk_..." \
-H "Content-Type: application/json" \
-d '{
"query": "how does this user like their responses?",
"top_k": 3,
"tags": ["user:alice"]
}'
Response
{
"query": "how does this user like their responses?",
"results": [
{
"id": "2026-04-17T14-22-03-8a4f2c",
"content": "User prefers concise answers in Spanish.",
"type": "preference",
"tags": ["user:alice", "lang:es"],
"metadata": {},
"created_at": 1744900923000,
"score": 0.0317,
"raw_score": 0.0317,
"boosted": false,
"channel_scores": { "vector": 0.871, "text": 2.14 }
}
],
"coarse_scanned": 523,
"fine_scanned": 100,
"text_scanned": 42,
"total_memories": 523,
"applied": {
"channels": ["vector", "text"],
"rrf": true, "mmr": true, "lambda": 0.7, "correction_boost": 2
},
"took_ms": {
"embed": 34, "coarse": 2, "load_full": 6, "fine": 6,
"text": 8, "fusion": 1, "mmr": 3, "total": 60
}
}
Note: when RRF is on, score is the fused RRF score (small numeric range, ~0.01–0.1). With RRF off, score is the cosine similarity (0–1). Use channel_scores to see each channel's raw contribution.
GET/v1/memories
List memories chronologically (newest first), cursor-paginated.
Query params: type, since (unix ms), limit (default 50, max 200), cursor.
curl -H "Authorization: Bearer ctxk_..." \
"https://memory.automators.work/v1/memories?type=preference&limit=20"
GET/v1/memories/:id
Fetch a specific memory by id.
curl -H "Authorization: Bearer ctxk_..." \
https://memory.automators.work/v1/memories/2026-04-17T14-22-03-8a4f2c
GET/v1/memories/:id/chain
Walk the supersession chain that contains this entry. Returns entries ordered from oldest to newest, following supersedes backwards and superseded_by forwards.
curl -H "Authorization: Bearer ctxk_..." \
https://memory.automators.work/v1/memories/2026-04-17T14-22-03-8a4f2c/chain
{
"anchor": "2026-04-17T14-22-03-8a4f2c",
"length": 3,
"chain": [
{ "id": "2026-04-15T...-a1", "content": "User prefers Rust",
"superseded_by": "2026-04-16T...-b2", ... },
{ "id": "2026-04-16T...-b2", "content": "User now prefers Go",
"supersedes": "2026-04-15T...-a1",
"superseded_by": "2026-04-17T14-22-03-8a4f2c", ... },
{ "id": "2026-04-17T14-22-03-8a4f2c", "content": "User switched back to Rust",
"supersedes": "2026-04-16T...-b2", ... }
]
}
DELETE/v1/memories/:id
Soft-delete (tombstone). The memory stops appearing in recalls and lists, but the event stays in history.
curl -X DELETE -H "Authorization: Bearer ctxk_..." \
https://memory.automators.work/v1/memories/2026-04-17T14-22-03-8a4f2c
POST/v1/forget
Forget multiple memories, either by explicit ids or by semantic query.
Mode 1 — explicit ids:
curl -X POST https://memory.automators.work/v1/forget \
-H "Authorization: Bearer ctxk_..." \
-H "Content-Type: application/json" \
-d '{ "ids": ["2026-04-17T14-22-03-8a4f2c", "2026-04-16T09-11-22-aabbcc"] }'
Mode 2 — semantic, with safety dry-run:
# First call: no confirm → returns matches without deleting
curl -X POST https://memory.automators.work/v1/forget \
-H "Authorization: Bearer ctxk_..." \
-H "Content-Type: application/json" \
-d '{ "query": "anything about old API keys" }'
# Second call: confirm:true → actually tombstones them
curl -X POST https://memory.automators.work/v1/forget \
-H "Authorization: Bearer ctxk_..." \
-H "Content-Type: application/json" \
-d '{ "query": "anything about old API keys", "confirm": true }'
Account & data
GET/v1/me
{
"user_id": "u_...",
"email": "you@example.com",
"plan": "free",
"status": "active",
"created_at": 1744900923000,
"created_at_iso": "2026-04-17T14:22:03.000Z"
}
GET/v1/stats
Counters for your memory store.
{
"total": 523,
"deleted": 12,
"by_type": { "note": 400, "preference": 80, "fact": 43 },
"oldest": 1700000000000,
"newest": 1744900923000,
"storage_bytes_estimate": 1847324
}
GET/v1/export
Downloads all your active (non-deleted) memories as newline-delimited JSON. No vendor lock-in — you can always take your data with you.
curl -H "Authorization: Bearer ctxk_..." \
https://memory.automators.work/v1/export > memories.jsonl
Limits & errors
Rate limits
| plan | requests / hour |
|---|---|
| free | 100 |
| pro | 10,000 |
| enterprise | 100,000 |
Every response includes X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers.
Error format
{ "error": "human-readable message" }
| status | meaning |
|---|---|
| 400 | Bad request (missing / invalid body) |
| 401 | Missing or invalid API key |
| 404 | Memory not found |
| 409 | Email already registered |
| 429 | Rate limit exceeded |
| 500 | Internal error |
Questions? Back home · API info JSON