REST API
API Reference
All endpoints accept and return JSON. Base URL: https://memory.wiki
Rate limit: 10 requests/min per IP. Max document size: 500KB.
/api/docsCreate a new document. Returns document ID, edit token, and creation timestamp.
Parameters
markdownREQUIREDstringThe Markdown content of the document.titlestringDocument title. If not provided, extracted from the first heading.isDraftbooleanDraft status. Default: false. Draft documents are only visible to the owner.sourcestringSource identifier: api, web, vscode, mcp, cli, or github:<owner>/<repo> for GitHub imports.editModestringWho can edit: owner (default for signed-in users), token (anyone with editToken), view (read-only for everyone but the owner).folderIdstringPlace the document in a specific folder.Request - curl
curl -X POST https://memory.wiki/api/docs \
-H "Content-Type: application/json" \
-d '{
"markdown": "# Hello World\nThis is my document.",
"title": "My Document",
"isDraft": false
}'Request - JavaScript
const res = await fetch("https://memory.wiki/api/docs", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
markdown: "# Hello World\nThis is my document.",
title: "My Document",
isDraft: false,
}),
});
const data = await res.json();Request - Python
import requests
res = requests.post("https://memory.wiki/api/docs", json={
"markdown": "# Hello World\nThis is my document.",
"title": "My Document",
"isDraft": False,
})
data = res.json()Response 200
{
"id": "abc123",
"editToken": "tok_aBcDeFgHiJkLmNoP",
"created_at": "2026-04-15T00:00:00Z"
}/api/docs/{id}Read a document by ID. Draft documents and email-restricted shares require owner or invitee authentication.
Headers (optional)
x-user-idstringUser UUID for ownership verification.x-user-emailstringUser email for identification (matches against the doc's allowed_emails / allowed_editors).AuthorizationstringBearer token for OAuth-authenticated requests.Request - curl
curl https://memory.wiki/api/docs/abc123Request - JavaScript
const res = await fetch("https://memory.wiki/api/docs/abc123");
const doc = await res.json();Request - Python
import requests
res = requests.get("https://memory.wiki/api/docs/abc123")
doc = res.json()Response 200
{
"id": "abc123",
"title": "My Document",
"markdown": "# Hello World\nThis is my document.",
"created_at": "2026-04-15T00:00:00Z",
"updated_at": "2026-04-15T01:00:00Z",
"view_count": 42,
"is_draft": false,
"editMode": "owner",
"isOwner": true,
"editToken": "tok_..."
}/api/docs/{id}Update a document. Requires edit token or owner authentication. Supports multiple actions: update content, soft-delete, rotate-token, change-edit-mode.
Parameters
editTokenREQUIREDstringThe edit token returned at creation (required for token mode).markdownstringNew Markdown content.titlestringNew document title.isDraftbooleanToggle between draft and published state.actionstringSpecial action: soft-delete, rotate-token.changeSummarystringVersion note describing the change.editModestringChange edit mode: owner, token, or view.Request - curl (update content)
curl -X PATCH https://memory.wiki/api/docs/abc123 \
-H "Content-Type: application/json" \
-d '{
"editToken": "tok_aBcDeFgH",
"markdown": "# Updated Content",
"changeSummary": "Fixed typos"
}'Request - curl (soft delete)
curl -X PATCH https://memory.wiki/api/docs/abc123 \
-H "Content-Type: application/json" \
-d '{
"editToken": "tok_aBcDeFgH",
"action": "soft-delete"
}'Request - JavaScript
const res = await fetch("https://memory.wiki/api/docs/abc123", {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
editToken: "tok_aBcDeFgH",
markdown: "# Updated Content",
}),
});Request - Python
import requests
res = requests.patch("https://memory.wiki/api/docs/abc123", json={
"editToken": "tok_aBcDeFgH",
"markdown": "# Updated Content",
})Response 200
{
"success": true,
"id": "abc123",
"updated_at": "2026-04-15T02:00:00Z"
}/api/docs/{id}Check when a document was last updated. Returns x-updated-at response header. Useful for sync polling without downloading full content.
Request - curl
curl -I https://memory.wiki/api/docs/abc123
# Response headers:
# x-updated-at: 2026-04-15T01:00:00Z
# x-content-length: 1234Request - JavaScript
const res = await fetch("https://memory.wiki/api/docs/abc123", {
method: "HEAD",
});
const updatedAt = res.headers.get("x-updated-at");Request - Python
import requests
res = requests.head("https://memory.wiki/api/docs/abc123")
updated_at = res.headers["x-updated-at"]/api/user/documentsList all documents owned by a user. Requires user identification via header.
Headers
x-user-idREQUIREDstringUser UUID. Alternatively use x-user-email or Authorization: Bearer.Request - curl
curl https://memory.wiki/api/user/documents \
-H "x-user-id: user-uuid-here"Request - JavaScript
const res = await fetch("https://memory.wiki/api/user/documents", {
headers: { "x-user-id": "user-uuid-here" },
});
const { documents } = await res.json();Request - Python
import requests
res = requests.get("https://memory.wiki/api/user/documents",
headers={"x-user-id": "user-uuid-here"})
documents = res.json()["documents"]Response 200
{
"documents": [
{
"id": "abc123",
"title": "My Document",
"created_at": "2026-04-15T00:00:00Z",
"updated_at": "2026-04-15T01:00:00Z",
"is_draft": false,
"view_count": 42
}
]
}/api/uploadUpload an image file. Returns a public URL. Accepts multipart form-data with a file field.
Request - curl
curl -X POST https://memory.wiki/api/upload \
-F "file=@screenshot.png"Request - JavaScript
const form = new FormData();
form.append("file", fileBlob, "screenshot.png");
const res = await fetch("https://memory.wiki/api/upload", {
method: "POST",
body: form,
});
const { url } = await res.json();Request - Python
import requests
with open("screenshot.png", "rb") as f:
res = requests.post("https://memory.wiki/api/upload",
files={"file": f})
url = res.json()["url"]Response 200
{
"url": "https://storage.memory.wiki/uploads/screenshot.png"
}/api/import/githubImport every .md file from a GitHub repo, folder, or single file. Caps: 80 files, 200KB per file. Creates one doc per file plus a bundle that groups them.
Parameters
urlREQUIREDstringGitHub URL — repo home, /tree/branch/path, /blob/branch/path, or raw.githubusercontent.com link.Request - curl
curl -X POST https://memory.wiki/api/import/github \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $JWT" \
-d '{ "url": "https://github.com/owner/repo/tree/main/docs" }'Response 200
{
"imported": 12,
"skipped": 0,
"deduplicated": 2,
"failed": 0,
"docs": [
{
"id": "abc123",
"title": "README",
"path": "README.md",
"sourceUrl": "https://github.com/owner/repo/blob/main/README.md"
}
]
}/api/import/obsidianImport every .md file from an Obsidian vault uploaded as a .zip (multipart/form-data, file field). Caps: 10MB zipped, 80 files, 200KB per file. Skips dot-prefixed folders (.obsidian, .git, …) and macOS resource forks.
Multipart fields
fileREQUIREDfileA .zip containing your vault. The file name (minus extension) is recorded as the vault label.Request - curl
curl -X POST https://memory.wiki/api/import/obsidian \
-H "Authorization: Bearer $JWT" \
-F "file=@MyVault.zip"Response 200
{
"imported": 12,
"skipped": 0,
"deduplicated": 2,
"failed": 0,
"docs": [
{
"id": "abc123",
"title": "Daily Note",
"path": "notes/Daily Note.md"
}
]
}/api/import/notionImport a single Notion page using the caller's internal integration token. v1 — no OAuth; the user pastes the token per call. Same dedup contract as docs/PDF/GitHub/Obsidian so re-calling is idempotent.
Parameters
tokenREQUIREDstringNotion internal integration token (secret_… or ntn_…). The integration must have access to the page.pageUrlstringNotion page URL — anything from notion.so/… with a UUID in it.pageIdstringAlternative to pageUrl. Hyphenated UUID or bare 32-char id.Request - curl
curl -X POST https://memory.wiki/api/import/notion \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $JWT" \
-d '{
"token": "secret_xxx",
"pageUrl": "https://www.notion.so/My-Page-abcdef0123456789abcdef0123456789"
}'Response 200
{
"imported": 1,
"deduplicated": 0,
"failed": 0,
"docs": [
{
"id": "abc123",
"title": "My Page",
"notionPageId": "abcdef01-2345-6789-abcd-ef0123456789",
"pageUrl": "https://www.notion.so/My-Page-abcdef0123456789abcdef0123456789"
}
]
}/api/hub/{slug}/recallHybrid retrieval over the public docs in a hub. Combines vector + keyword search; pass rerank: true to reorder candidates with a Haiku-based cross-encoder for higher precision.
Parameters
queryREQUIREDstringThe natural-language question or search phrase.knumberNumber of results to return. Default 8.rerankbooleanWhen true, fetch k * 4 candidates first then rerank with Anthropic Haiku. Slower but more precise.Request - curl
curl -X POST https://memory.wiki/api/hub/your-slug/recall \
-H "Content-Type: application/json" \
-d '{ "query": "how do bundles work?", "k": 6, "rerank": true }'Response 200
{
"matches": [
{
"doc_id": "abc123",
"title": "Bundles overview",
"passage": "A bundle groups related docs into…",
"score": 0.84
}
],
"meta": { "reranked": true }
}Raw + /llms.txt
Every public Memory.Wiki URL also exposes a clean-markdown variant for AI agents. Append ?compact or ?digest to cut tokens — the answer is the same; the bill is smaller.
/raw/{id}GETPlain markdown for a single document./raw/b/{bundleId}GETBundle digest: frontmatter + the canvas analysis (themes, insights, concept sub-graph) + a numbered link list of member docs. Add ?full=1 to inline every doc body, ?graph=0 to drop the analysis. See Bundle URL anatomy below./raw/hub/{slug}GETWhole-hub markdown. ?digest=1 returns a concept-clustered summary./raw/hub/{slug}/c/{concept}GETPer-concept passage page across all docs in the hub./hub/{slug}/llms.txtGETManifest the agent can fetch first to understand what's available./hub/{slug}/llms-full.txtGETDense whole-hub bundle (default 80k tokens, override with ?cap=).Bundle URL anatomy
Pasting a bundle URL (memory.wiki/b/{id}) into Claude, ChatGPT, or Cursor returns more than a doc list. The default response is a digest that also carries the canvas's cross-document analysis — themes, insights, gaps, takeaways, notable connections, and a concept sub-graph — so the consuming AI inherits the prior AI's work instead of redoing it.
Vector embeddings stay server-side (they're numeric and unusable to an LLM). What ships over the wire is the textual output the analysis already produced.
Response shape
---
mw_bundle: 1
id: <bundleId>
title: "..."
url: https://memory.wiki/b/<id>
document_count: N
updated: <ISO>
analysis_generated_at: <ISO> # present when the canvas has run
analysis_stale: true # only when a member doc was edited after that run
source: "Memory.Wiki"
---
# <Bundle title>
> <description>
**Intent:** <intent>
> ⚠ _Analysis may be stale — re-run the canvas to refresh._ (stale only)
## Summary
<canvas summary>
## Themes
- ...
## Cross-document insights
- ...
## Key takeaways
- ...
## Open questions / gaps
- ...
## Notable connections
- **doc A title** ↔ **doc B title** — <relationship>
## Concepts (this bundle)
- **concept label** (from **doc title**)
## Concept relations
- **conceptA** ↔ **conceptB** — <edge label>
1. [Doc 1 title](https://memory.wiki/<docId>) — annotation
2. [Doc 2 title](https://memory.wiki/<docId>) — annotationQuery parameters
fullboolean?full=1 inlines every member doc's full markdown body after the doc-list section. Default (omitted) returns the link list only — the AI follows links on demand.graphboolean?graph=0 (also false / off) drops the canvas-analysis sections and returns the legacy doc-list-only digest. Useful when the analysis is heavy or the caller only needs the inventory.compactboolean?compact strips redundant whitespace and trims long quoted blocks across the whole payload. Combines with full and graph.Two ontology layers
Memory.Wiki ships two distinct ontology layers, and each lives on its own URL:
- concept_index — hub-wide. Concept labels + the docs each appears in. Already embedded in
/raw/hub/{slug}. - ai_graph — bundle-scoped. Themes, insights, gaps, connections, and a concept sub-graph produced by the canvas. Embedded in
/raw/b/{id}as of this release.
Staleness model
The canvas analysis is a snapshot. If any member doc's updated_at is later thangraph_generated_at, the response sets analysis_stale: true in frontmatter and prepends a warning blockquote so the consuming AI can weight the analysis appropriately.
Authentication
Memory.Wiki uses progressive authentication. Basic operations require no auth. Advanced features use edit tokens or user identity.
No auth required
POST /api/docs and GET /api/docs/{id} work without authentication. The returned editToken is your proof of ownership.
Edit tokens
Every document gets an editToken at creation. Include it in PATCH requests to update or delete. MCP server and CLI manage tokens automatically.
User identity
For user-scoped actions (list, ownership), provide x-user-id header, x-user-email header, or Authorization: Bearer JWT token.
MCP / CLI auth
Both MCP server and CLI use JWT from Memory.Wiki login. Run: npm install -g memory-wiki-cli && Memory.Wiki login
Rate Limits
All endpoints are rate-limited to 10 requests per minute per IP. Exceeding the limit returns 429 Too Many Requests. The response includes Retry-After header indicating seconds to wait. Maximum document size is 500KB.
Errors
400errorBad Request. Missing required fields or invalid parameters.401errorUnauthorized. Invalid or missing edit token / credentials.403errorForbidden. Insufficient permissions for this resource.404errorNot Found. Document does not exist or has been deleted.409errorConflict. Anti-template guard refused the write (would overwrite real content with boilerplate).429errorToo Many Requests. Rate limit exceeded.500errorInternal Server Error. Please retry or contact support.Error Response Format
{
"error": "Document not found",
"status": 404
}