Skip to content

API Reference

Rahil P edited this page Jun 7, 2026 · 4 revisions

All endpoints require an Authorization: Bearer YOUR_TOKEN header except CORS preflight.

Error responses are standardized as { "ok": false, "error": "..." } across all routes (e.g. 401 { "ok": false, "error": "Unauthorized" }).


POST /capture

Store an entry. Duplicate detection runs synchronously. Embedding happens in the background so the response is instant.

Request body:

{
  "content": "your note here",
  "tags": ["work", "idea"],
  "source": "api"
}
Field Required? Default Description
content Required The text to store. Rejected if missing or empty.
tags Optional [] Array of tags to attach to the entry
source Optional "api" Free-text label for where the note came from

Responses:

Stored successfully:

{ "ok": true, "id": "uuid-v4" }

Stored but similar entry exists (tagged duplicate-candidate):

{
  "ok": true,
  "id": "uuid-v4",
  "warning": "similar",
  "matchId": "existing-uuid",
  "score": 88.5,
  "message": "Stored but similar entry exists — tagged as duplicate-candidate"
}

Blocked — near-exact duplicate:

{
  "ok": false,
  "duplicate": true,
  "matchId": "existing-uuid",
  "score": 97.2,
  "message": "Near-exact duplicate detected — not stored"
}

Merged into an existing entry (no new row created):

{ "ok": true, "id": "existing-uuid", "action": "merged", "message": "Memories merged into a single combined entry" }

Replaced an outdated existing entry:

{ "ok": true, "id": "existing-uuid", "action": "replaced", "message": "New memory replaced an outdated existing entry" }

Resolved a contradiction (old conflicting entry deleted, new one stored and tagged contradiction-resolved):

{ "ok": true, "id": "uuid-v4", "resolved_conflict": "deleted-entry-uuid", "reason": "The new note says the meeting moved to Wednesday, superseding the Tuesday entry." }

See How It Works → Duplicate detection, contradictions, and smart merging for when each of these outcomes (merged/replaced/contradiction/flagged/blocked) gets triggered.

Status Meaning
200 ok:true Stored, merged, replaced, or contradiction resolved
200 ok:false duplicate:true Blocked — near-exact duplicate
400 Missing or invalid content
401 Invalid auth token

POST /append

Append new information to an existing entry. The original content is preserved and the update is added with a timestamp.

Request body:

{
  "id": "existing-entry-uuid",
  "addition": "New information to append"
}
Field Required? Description
id Required UUID of the entry to append to. Rejected (400) if missing/empty.
addition Required Text to append. Rejected (400) if missing/empty.

Responses:

Success:

{
  "ok": true,
  "id": "existing-entry-uuid",
  "message": "Update appended successfully with timestamp"
}

Entry not found:

{
  "ok": false,
  "error": "No entry found with ID: existing-entry-uuid"
}

Append failed:

{
  "ok": false,
  "error": "Append failed: <error details>"
}
Status Meaning
200 ok:true Update appended successfully
404 ok:false Entry not found
500 ok:false Append operation failed
400 Missing or invalid id or addition
401 Invalid auth token

GET /list

List recent entries in reverse chronological order, with optional tag and time-range filters.

curl "https://<your-worker-url>/list?n=20&tag=work&after=1700000000000&before=1750000000000" \
  -H "Authorization: Bearer YOUR_TOKEN"

All query parameters are optional.

Query param Required? Default Max Description
n Optional 20 100 Number of entries to return
tag Optional — (no filter) Only return entries with this exact tag
after Optional — (no filter) Only return entries created at/after this Unix ms timestamp
before Optional — (no filter) Only return entries created at/before this Unix ms timestamp

Returns a JSON array of entry objects (same filtering behavior as the list_recent MCP tool).


GET /recall

Semantic search over your memories — vector search with time-decay reranking, chunk dedup, and an LLM-synthesized insight. Mirrors the recall MCP tool.

curl "https://<your-worker-url>/recall?query=what+did+I+decide+about+pricing&topK=5&tag=work" \
  -H "Authorization: Bearer YOUR_TOKEN"
Query param Required? Default Range Description
query Required Natural-language search query. Returns 400 if missing/empty.
topK Optional 5 clamped to 120 Max number of results to return
tag Optional — (no filter) Restrict results to entries carrying this exact tag
after Optional — (no filter) Only consider entries created at/after this Unix ms timestamp
before Optional — (no filter) Only consider entries created at/before this Unix ms timestamp

Responses:

Matches found:

{
  "ok": true,
  "results": [
    {
      "id": "entry-uuid",
      "content": "Decided to go with usage-based pricing...",
      "score": 91.4,
      "tags": ["work", "pricing"],
      "source": "api",
      "created_at": 1718000000000,
      "updated": false
    }
  ],
  "insight": "You've been leaning toward usage-based pricing models." 
}

Nothing found:

{ "ok": true, "results": [], "message": "Nothing found matching that query." }
Status Meaning
200 ok:true Search executed (results may be empty)
400 ok:false Missing or empty query
401 Invalid auth token

POST /forget

Permanently delete an entry and all of its associated vectors (original + chunked updates) from D1 and Vectorize. Mirrors the forget MCP tool.

Request body:

{ "id": "existing-entry-uuid" }
Field Required? Description
id Required UUID of the entry to delete. Rejected (400) if missing/empty. Whitespace is trimmed before lookup.

Responses:

Success:

{ "ok": true, "id": "existing-entry-uuid", "deletedVectors": 2 }

Entry not found:

{ "ok": false, "error": "No entry found with ID: existing-entry-uuid" }
Status Meaning
200 ok:true Entry and vectors deleted
400 ok:false Missing or invalid id, or invalid JSON body
404 ok:false No entry found with that ID
401 Invalid auth token

Note: Vectorize cleanup is non-fatal — if vector deletion fails, the D1 row is still removed and the response still reports success.


POST /update

Replace an entry's content (re-extracts hashtags as tags and re-embeds it). Unlike /append, this overwrites rather than preserving the original text.

Request body:

{ "id": "existing-entry-uuid", "content": "the corrected note text" }
Field Required? Description
id Required UUID of the entry to overwrite. Rejected (400) if missing/empty.
content Required New content. Replaces the existing text entirely (hashtags are re-extracted into tags, and the entry is re-embedded). Rejected (400) if missing/empty.

Responses:

Success:

{ "ok": true, "id": "existing-entry-uuid", "vectors": 1 }

Entry not found:

{ "ok": false, "error": "No entry found with ID: existing-entry-uuid" }
Status Meaning
200 ok:true Entry updated and re-embedded
400 ok:false Missing id/content, or invalid JSON body
404 ok:false No entry found with that ID
401 Invalid auth token

GET /count

Returns the total number of stored entries. No parameters.

curl "https://<your-worker-url>/count" -H "Authorization: Bearer YOUR_TOKEN"

Response:

{ "count": 142 }

GET /tags

Returns every distinct tag currently in use, alphabetically sorted. No parameters.

curl "https://<your-worker-url>/tags" -H "Authorization: Bearer YOUR_TOKEN"

Response:

["idea", "pricing", "work"]

GET /stats

Returns summary statistics about your second brain — entry count, average importance score, top tags, and tags that are candidates for digest synthesis (high-volume tags without a recent digest). No parameters.

curl "https://<your-worker-url>/stats" -H "Authorization: Bearer YOUR_TOKEN"

Response:

{
  "count": 142,
  "avg_importance": 2.3,
  "top_tags": ["work", "idea", "pricing", "personal", "claude-response"],
  "digest_candidates": [
    { "tag": "work", "count": 27 }
  ]
}

POST /chat

Streams an LLM-generated answer to a question, grounded in a set of memories you provide (typically the results of a prior /recall call). Used by the web dashboard's chat feature.

Request body:

Field Required? Description
query Required The question to answer. Rejected (400) if missing/empty.
memories Optional A text blob of relevant memories to ground the answer in (e.g. formatted /recall results)

Response: a text/event-stream of the LLM's streamed response (Server-Sent Events) — not a single JSON payload.

Status Meaning
200 Streaming response started
400 ok:false Missing query, or invalid JSON body
401 Invalid auth token

GET /digest

Synthesizes a high-volume tag's entries into a single condensed summary entry (tagged synthesized), to keep recall results from being dominated by repetitive low-value notes. This is the same compression the nightly cron job runs automatically — /digest lets you trigger it on demand for a specific tag.

Query param Required? Description
tag Required The tag to compress. Returns 400 if missing/empty.

Responses:

Digest created:

{
  "tag": "work",
  "synthesis": "Condensed summary text...",
  "entry_id": "new-synthesized-entry-uuid",
  "source_count": 23
}

Not enough entries, or compressed too recently:

{
  "tag": "work",
  "error": "Could not create digest — tag may have fewer than 20 entries or was recently compressed",
  "source_count": 8
}
Status Meaning
200 Either a digest was created, or a reason is returned for why one wasn't (check for the error field)
400 ok:false Missing tag
401 Invalid auth token

GET+POST /mcp

MCP server endpoint using the Streamable HTTP transport. Authentication is required — either:

  • An Authorization: Bearer <your-token> header (your AUTH_TOKEN), or
  • A ?token=<your-token> query param (less secure — can leak via browser history/logs; prefer the header where supported), or
  • A full OAuth flow via /oauth/authorize (for clients that support OAuth, e.g. browser-based MCP clients)

Connect any MCP-compatible client using:

https://<your-worker-url>/mcp

See Connect to AI Clients for client-specific setup instructions and config examples.

MCP tools

? after a parameter name means it's optional. All others are required.

Tool Parameters Description
remember content, tags? (array), source? Store a note with duplicate detection
append id, addition Append new info to an existing entry, preserving the original
update id, content Replace an entry's full content (overwrites, re-embeds)
recall query, topK? (default 5, range 120), tag?, after? (Unix ms), before? (Unix ms) Semantic search with chunk deduplication and time-decay reranking
list_recent n? (default 10, max 50), tag?, after? (Unix ms), before? (Unix ms) Chronological listing
forget id Delete entry and all chunks from D1 and Vectorize

Note: recall's REST counterpart (GET /recall) clamps topK to 120 with a default of 5; list_recent's REST counterpart (GET /list) defaults n to 20 with a max of 100 (the MCP tool itself defaults to 10/max 50).


GET /

Returns the web dashboard (single-page HTML app). No auth required — auth is handled client-side via localStorage.

Clone this wiki locally