api reference
The whole surface fits on one page.
One base URL, bearer keys, JSON in and out. Facts and documents share the same container, so a single search can rank both. If you only read one section, read quickstart.
quickstart
two requestsClaim a key at /claim. That page asks you to sign in with Cloudflare Access and a verified email before it mints anything; the API's claim endpoint is not public. If you already have a free container, sign in with the email you used for it: a paid checkout with a different address is linked to that container when you claim the paid key. Send one piece of text, then ask a question with the key.
curl -X POST https://thin-memory.parithosh.workers.dev/v1/add \
-H "Authorization: Bearer tmk_..." \
-H "Content-Type: application/json" \
-d '{"content":"Amanda leads design in Lisbon and prefers Rust."}' the response includes the facts that were extracted
curl -X POST https://thin-memory.parithosh.workers.dev/v1/search \
-H "Authorization: Bearer tmk_..." \
-H "Content-Type: application/json" \
-d '{"q":"what does amanda prefer?","limit":5}'
For the start of a conversation, call GET /v1/profile. It returns the
stable facts ranked by recall plus the most recent memories, and a ready-to-paste
context block.
curl https://thin-memory.parithosh.workers.dev/v1/profile \
-H "Authorization: Bearer tmk_..." auth + containers
keys share one namespace
Send the key as Authorization: Bearer tmk_... (or X-API-Key).
A service key is pinned to exactly one container tag: a namespace such as
user_amanda or project_atlas. Every read and write is scoped to
it in SQL, not in the prompt.
- Service key —
tmk_…· one container. Mint one per user or per integration. - Master key —
tmm_…· worker secret. Mints keys and may address any container withcontainerTag. - Storage · only the SHA-256 of a key is stored. The token is shown exactly once.
Rotating a key means minting a new one and deleting the old. The container and its memory are untouched. A service key can manage keys in its own container; the master key can manage every container.
usage + key management
curl https://thin-memory.parithosh.workers.dev/v1/usage \
-H "Authorization: Bearer tmk_..." plan, period, used, limit and remaining for adds, searches and extractions
curl https://thin-memory.parithosh.workers.dev/v1/keys \
-H "Authorization: Bearer tmk_..." metadata only — token hashes are never returned
# mint a second key for this container, then revoke the old one
curl -X POST https://thin-memory.parithosh.workers.dev/v1/keys \
-H "Authorization: Bearer tmk_..." \
-H "Content-Type: application/json" \
-d '{"name":"laptop-2"}'
curl -X DELETE https://thin-memory.parithosh.workers.dev/v1/keys/<old-key-id> \
-H "Authorization: Bearer tmk_..." keys are capped per container; revoke the old key after the new one works
mcp
streamable http · stateless
The MCP endpoint lives at https://thin-memory.parithosh.workers.dev/mcp and accepts the same service keys.
It is stateless streamable HTTP, so there is no session to keep alive and no SSE
reconnect loop to babysit.
{
"mcpServers": {
"memory": {
"url": "https://thin-memory.parithosh.workers.dev/mcp",
"headers": { "Authorization": "Bearer tmk_..." }
}
}
} {
"mcp": {
"servers": {
"memory": {
"type": "remote",
"url": "https://thin-memory.parithosh.workers.dev/mcp",
"oauth": false,
"headers": { "Authorization": "Bearer tmk_..." }
}
}
}
} | tool | what it does |
|---|---|
add_memory | store text and return extracted facts |
search_memory | ranked facts and documents for a query |
context | start-of-conversation block, optional query |
list_memories | recent memories and document ids |
delete_memory | forget one fact or one document |
Claude Code, one line:
claude mcp add --transport http memory https://thin-memory.parithosh.workers.dev/mcp --header "Authorization: Bearer tmk_..." Every agent at once, with the CLI:
# save the key, then wire every agent found on this machine
npx -y thin-memory login tmk_...
npx -y thin-memory init --yes
Detects and configures Claude Code, OpenCode, Codex CLI, Gemini CLI, Cursor,
VS Code, Windsurf, Cline/Roo, and appends a memory rules block to
AGENTS.md / CLAUDE.md. Zed, Continue, JetBrains and
Amp get printed instructions.
Agent that only launches a command (stdio):
{
"mcpServers": {
"memory": {
"command": "npx",
"args": ["-y", "thin-memory", "mcp"],
"env": { "THIN_MEMORY_KEY": "tmk_..." }
}
}
} rest
six calls that matter| method | path | notes |
|---|---|---|
| POST | /v1/add | store text; chunks + extraction |
| POST | /v1/memories | store one fact as-is, no extraction |
| POST | /v1/search | q, limit, searchMode |
| GET | /v1/profile | stable facts + recent memories + context |
| GET | /v1/items | list memories and documents with ids |
| DELETE | /v1/documents/:id | delete a document and its chunk vectors |
| DELETE | /v1/memories/:id | forget one fact |
| GET | /v1/search?q= | same as POST, for quick queries |
Add → response
{
"id": "doc_7f3a91",
"containerTag": "user_amanda",
"chunks": 1,
"chars": 57,
"memories": ["m_2b8c", "m_9d10"],
"extracted": [
"Amanda leads design in Lisbon.",
"Amanda prefers Rust."
]
} extractionSkipped appears when the monthly extraction budget is spent
Search → response
{
"q": "what does amanda prefer?",
"searchMode": "hybrid",
"results": [
{
"id": "m_9d10",
"kind": "memory",
"text": "Amanda prefers Rust.",
"score": 0.9124
}
]
} results also carry documentId, containerTag and createdAt
Profile → response
{
"containerTag": "user_amanda",
"counts": { "memories": 38, "documents": 4 },
"static": ["Amanda prefers Rust.", "..."],
"dynamic": [{ "id": "m_9d10", "text": "...", "at": "2026-10-22T14:03:00Z" }],
"context": "Memory container: user_amanda\n\n## Stable facts\n- ..."
} search
three lanes, one ranking- hybrid · the default. Extracted facts and document chunks in one ranked list.
- memories · facts only: "what do I know about this user?"
- documents · source chunks only: classic retrieval for long text.
Rankings combine vector similarity with recency. Container containment is applied during hydration in D1, and a local re-embedding lane covers Vectorize's asynchronous settle window, so an add is recallable immediately.
limit defaults to 8 and is capped at 50. Search is quota-metered per
container; one call is one search regardless of the limit.
limits + errors
predictable shapes
Errors are JSON: {"error":{"type":"...","message":"..."}}. A quota
rejection adds the plan, counters and an upgrade URL, and returns 429.
Check GET /v1/usage before you hit it.
{
"error": {
"type": "quota_exceeded",
"message": "monthly add limit reached for free",
"plan": "free",
"limit": 25,
"used": 25,
"upgrade": "https://thin-memory.pages.dev/pricing"
}
} - 401 · missing or unknown bearer key.
- 403 · a service key tried to address another container or an admin route, or a claim was attempted outside /claim.
- 404 · unknown id, document/fact outside the key's container, or a key/subscription that does not match.
- 409 · the container reached its key cap. Revoke an old key first.
- 429 · add/search quota spent, or too many claim attempts. Check
Retry-After. - 503 · storage or index unavailable. Safe to retry with backoff.
Quotas reset on the first of each month, UTC. Extraction is soft-gated: an add still
stores the source when the extraction budget is spent, and the response carries
extractionSkipped: true.