Documentation

Ahok Memory docs

Long-term memory for AI agents: API keys, the MCP server, the REST API, workspaces, plans and admin access.

Getting started

Ahok Memory is hosted long-term memory for AI agents. Agents store short facts, recall them later by search, and keep them organized in workspaces. Everything runs against the API at https://memtool.ahok.io.

1. Get your API key

Sign in with Google, then copy your key from Settings › API key. Keep it in an environment variable, never in prompts, code or git.

bash
export AHOK_API_KEY="opm_..."

2. Store a memory

bash
curl -s https://memtool.ahok.io/memory/add \
  -H "x-api-key: $AHOK_API_KEY" -H "content-type: application/json" \
  -d '{"content": "Thierry prefers dark roast coffee with oat milk.", "tags": ["preferences"]}'

3. Recall it

bash
curl -s https://memtool.ahok.io/memory/query \
  -H "x-api-key: $AHOK_API_KEY" -H "content-type: application/json" \
  -d '{"query": "how does Thierry take his coffee", "k": 5}'

GET https://memtool.ahok.io/health needs no key and answers 200 when the service is up.

API keys

Every request except GET /health needs a key, sent as x-api-key: <key> or Authorization: Bearer <key>.

opm_… (account key)Acts as you. Memories it adds have no workspace unless you pass memory_key_id. Rotate it in Settings › API key.
opm_sk_… (workspace key)Acts as you, inside one workspace. Memories it adds go into that workspace.
opm_admin_… (admin key)Admins only. Unlimited, and can act on every account. See Admin access.

An account or workspace key always acts as its owner. A user_id you send is ignored, so one account can never read or write another account's memories.

MCP & IDE integration

The hosted MCP endpoint is https://memtool.ahok.io/mcp (Streamable HTTP). It needs your key in a header.

Claude Code

bash
claude mcp add --transport http ahok-memory https://memtool.ahok.io/mcp --header "x-api-key: $AHOK_API_KEY"

Cursor, Windsurf and other clients with URL and headers

json
{
  "mcpServers": {
    "ahok-memory": {
      "type": "http",
      "url": "https://memtool.ahok.io/mcp",
      "headers": { "x-api-key": "YOUR_AHOK_API_KEY" }
    }
  }
}

Clients that only run local servers (Claude Desktop)

json
{
  "mcpServers": {
    "ahok-memory": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://memtool.ahok.io/mcp", "--header", "x-api-key:${AHOK_API_KEY}"],
      "env": { "AHOK_API_KEY": "YOUR_AHOK_API_KEY" }
    }
  }
}

Do not use npx openmemory-js mcp: it starts a separate local server with its own database, so nothing reaches your account.

Tools

openmemory_storeSave a memory: content, tags?, metadata?
openmemory_querySearch: query, k? (1–32, default 8), sector?, min_salience?
openmemory_listMost recent memories: limit? (1–50, default 10), sector?
openmemory_getOne memory by id.
openmemory_reinforceRaise a memory's importance: id, boost? (0.01–1, default 0.1)

REST API

Add a memory

http
POST /memory/add
{ "content": "Thierry prefers dark roast coffee.", "tags": ["preferences"], "metadata": { "source": "chat" }, "memory_key_id": "optional-workspace-id" }

200 { "id": "…", "primary_sector": "semantic", "sectors": ["semantic"], "chunks": 1 }

Storing identical content again returns the existing memory with "deduplicated": true and reinforces it. memory_key_id must be one of your workspaces.

Search

http
POST /memory/query
{ "query": "deploy schedule", "k": 8, "filters": { "sector": "procedural", "min_score": 0.2 } }

POST /query          (simple form, also returns "result" as text)
{ "query": "how does Thierry take his coffee", "k": 5 }

Both return matches: [{ id, content, score, primary_sector, sectors, last_seen_at }]. Search covers every workspace in your account.

Read, update, delete

GET /memory/{id}One memory.
PATCH /memory/{id}{ content?, tags?, metadata? }
DELETE /memory/{id}Delete one memory. This frees capacity at once.
POST /memory/reinforce{ id, boost }
POST /memory/bulk-delete{ ids: [...] } returns { deleted, failed }
POST /memory/bulk-move{ ids, target_workspace_id } (null removes the workspace)
GET /memory/all?l=100&u=0List, newest first. l = limit, u = offset; also key_id and sector filters.

Ingest documents

POST /memory/ingest with { content_type, data } (txt, md, html as text; pdf and docx as base64), or POST /memory/ingest/url with { url }.

Workspaces

Give each agent or project its own workspace key, so its memories are filed together.

GET /keysYour workspaces: { keys: [{ id, label, secret_key, created_at }] }
POST /keys{ label } creates one and returns its secret_key.
PATCH /keys/{id}{ label } renames it.
DELETE /keys/{id}Deletes it. Its key stops working; its memories stay in your account.
GET /keys/statsMemory counts per workspace.

Writing memories that recall well

One fact, ≤ 200 charactersEach memory keeps its first 200 characters. Split documents into self-contained sentences.
Name the subject"Thierry's flight to Lisbon is on 12 December", not "It's on the 12th".
Use search wordsSearch matches words, so include the keywords and synonyms people will ask with.
Tag and add metadataTags for sections or sources; structured details in metadata.
Update, don't contradictEdit or delete stale facts instead of adding conflicting ones.

How recall works

Each memory is classified into a sector: semantic (facts, preferences), episodic (events), procedural (how-to), emotional or reflective. Use the sector filter to narrow a search.

Results are ranked by match, recency and importance. Memories you recall or reinforce rise; memories nobody uses slowly decay. Related memories are linked, so a search can surface neighbours of a strong match.

Plans and limits

Free1,000 memories.
Starter10,000 memories. €20 per month, or €200 per year, VAT included.
Pro100,000 memories. €100 per month, or €1,000 per year, VAT included.
EnterpriseCustom limits, set with you.

At the limit, adding a memory answers 403 capacity_exceeded (MCP returns a tool error). Reading, editing and deleting keep working, and deleting frees capacity at once. Change plans in Settings › Billing: upgrades apply immediately, downgrades at the end of the billing period.

Admin access

Admin accounts see an Admin page in the dashboard. From there they create admin API keys, manage users and their plans, and grant admin to others.

UnlimitedAdmin accounts have no memory limit.
Every accountAn admin key can read and write any account. Pass user_id to act on a user; without it, writes go to your own account and searches cover every account.
Admin-only routes/users, /dashboard, /sectors, /dynamics, /api/temporal, /api/compression and /sources accept admin keys.
Admin console APIGET /account/admin/users, GET|PATCH|DELETE /account/admin/users/{id}, GET|POST /account/admin/keys, DELETE /account/admin/keys/{id}
SafetyKeys are shown once and stored hashed. Revoke a key, or remove its owner's admin access, and it stops working at once.
bash
curl -s "https://memtool.ahok.io/account/admin/users?search=example.com" -H "x-api-key: $AHOK_ADMIN_KEY"

Errors

400 { err: "content" }Missing or invalid field.
401 authentication_requiredNo API key.
403 invalid_api_keyUnknown, rotated or revoked key.
403 { err: "forbidden" }Not your memory or workspace.
403 capacity_exceededMemory limit reached. Upgrade or delete memories.
403 admin_requiredAdmin-only route.
404 { err: "nf" }No such memory.

Code examples

Python

python
import os, requests

BASE = "https://memtool.ahok.io"
H = {"x-api-key": os.environ["AHOK_API_KEY"]}

def remember(content, tags=None):
    return requests.post(f"{BASE}/memory/add", headers=H, json={"content": content, "tags": tags or []}, timeout=30).json()

def recall(query, k=5):
    r = requests.post(f"{BASE}/memory/query", headers=H, json={"query": query, "k": k}, timeout=30)
    return [m["content"] for m in r.json().get("matches", [])]

TypeScript

typescript
const BASE = "https://memtool.ahok.io";
const headers = { "content-type": "application/json", "x-api-key": process.env.AHOK_API_KEY! };

export async function remember(content: string, tags: string[] = []) {
  const res = await fetch(`${BASE}/memory/add`, { method: "POST", headers, body: JSON.stringify({ content, tags }) });
  return res.json();
}

export async function recall(query: string, k = 5): Promise<string[]> {
  const res = await fetch(`${BASE}/memory/query`, { method: "POST", headers, body: JSON.stringify({ query, k }) });
  return ((await res.json()).matches ?? []).map((m: { content: string }) => m.content);
}

System prompt snippet

text
You have long-term memory through Ahok Memory (tools openmemory_query and openmemory_store).
- At the start of a task, search memory for context about the user and the task.
- When you learn a durable fact or preference, store it as one short sentence that names its subject.
- Don't store secrets, passwords or API keys.

FAQ

Can another account see my memories?

No. Every memory belongs to one account and every key acts as its owner. Only admin keys can reach other accounts.

Why did a search miss a memory?

Search matches words. Rephrase with words the memory contains, or store facts with the keywords people will search for.

What happens when I downgrade?

You keep your plan until the end of the billing period. If you are above the new limit, new memories stop saving until you delete some; nothing is deleted for you.

How do I export or delete my data?

Settings › Account downloads every memory as JSON, or deletes the account and all its data.