# Wontopos (WOS) - Long-term memory for AI agents # Base URL: https://api.wontopos.com · Console: https://wontopos.com/console · Status: https://status.wontopos.com # OpenAPI 3.1 spec: https://api.wontopos.com/openapi.json · MCP server: npx -y wontopos-mcp # Want more than this file? The full docs are server-rendered and crawlable: https://wontopos.com/en/why # Launched 2026-07-06 · Last updated Jul 28, 2026 (New York). This file updates with each release # and is the source of truth (the on-site AI guide may lag 1-3 days). # Drop this file into your IDE or coding agent and build. > WOS stores an end-user's memories once, then recalls only the relevant ones per > query so you can feed them into an LLM prompt. Retrieval is pure semantic search > (embeddings only) - no keyword/BM25 matching, and no LLM ever runs over your stored > memories - so no language is privileged: store and query in any language, mixed > freely. Each query returns a > small, bounded context (~1,200 tokens median, never >1,700) no matter how much is > stored; p50 ~320ms. We never train on, view, or use your data. ## Start here - auth is TWO things 1) API key: header `X-API-Key: wos-live-...` on every request (create one in the console). 2) A store (`user_id`): every call is scoped to one store, which isolates each end-user's memories. Stores are EXPLICIT - writing to or reading a store that does not exist returns 404. Every account starts with a `default` store; create more with POST /api/v1/memory/collection. Model: header `X-WOS-Model: tablet-1` (the default). Live models share one memory per account - store with one, recall with another. Workspaces (console) isolate memory, keys, and usage: the same store id in two workspaces is two separate memory spaces. ## Quickstart # curl: create the store once -> remember -> recall curl -s https://api.wontopos.com/api/v1/memory/collection \ -H "X-API-Key: wos-live-..." -H "Content-Type: application/json" -d '{"user_id":"alice"}' curl -s https://api.wontopos.com/api/v1/memory/store \ -H "X-API-Key: wos-live-..." -H "Content-Type: application/json" \ -d '{"user_id":"alice","content":"she prefers tea over coffee"}' curl -s https://api.wontopos.com/api/v1/memory/recall \ -H "X-API-Key: wos-live-..." -H "Content-Type: application/json" \ -d '{"user_id":"alice","query":"what does alice drink?"}' # Python (TypeScript / Rust: identical surface - see SDKs / MCP below) from wontopos import Client mem = Client(api_key="wos-live-...", user_id="alice") # store set once; model defaults to tablet-1 mem.create_store() # stores are explicit (or use "default") mem.add("she prefers tea over coffee") ctx = mem.recall("what does alice drink?") # {short_term, long_term, context} # put ctx["context"] into your LLM prompt ## Endpoints (POST JSON unless noted; every call needs X-API-Key + a user_id) - /api/v1/memory/store {user_id, content, metadata?} -> {id, status} - /api/v1/memory/search {user_id, query, max_results?=10, cache_control?, speaker?, filters?, form?, tz?} -> {memories:[{id,content,similarity,importance,category,time_bucket?,is_superseded,superseded_by,speaker?,time?}]} # ALREADY ordered best-first - do not re-sort. `similarity` is the raw vector score, NOT the # ranking key (that is the engine's reranker, which is internal), so sorting by it makes results worse. # `memories[0]` is the best match; there is no `score` field. - /api/v1/memory/recall {user_id, query, form?, tz?} -> {short_term, long_term, context} # one-call LLM context; form renders each long-term memory's time - /api/v1/memory/store-turn {user_id, user_msg, assistant_msg} -> {status} # remember a chat exchange - /api/v1/memory/bulk-store {user_id, content, category?, timestamp?} -> {status} # backfill a large blob - /api/v1/memory/supersede {user_id, old_memory_id, new_content} -> {old_memory_id, new_memory_id, status} # update an out-of-date memory - /api/v1/memory/forget {user_id, memory_id?} -> {status} # omit memory_id = erase the whole user (GDPR) - /api/v1/memory/stats {user_id} -> {total_memories, short_term_turns} - /api/v1/memory/history {user_id} -> {turns:[...]} - /api/v1/memory/list {user_id, limit?=100, cursor?} -> {memories:[{id,content,category,source_type,created_at,event_date,is_superseded}], count, next_cursor} # raw memories (original text, no vectors), paged by cursor - /api/v1/memory/get {user_id, memory_id} -> {user_id, memory:{id,content,category,source_type,created_at,event_date,is_superseded}} # ONE memory by the id store/list returned (no vector; other store / deleted / invalidated -> 404) - /api/v1/memory/collection {user_id} POST = create a store · DELETE = drop the store and ALL its memories - /api/v1/memory/collections (GET) -> {collections:[{user_id, created_at}], count} - /api/v1/memory/speakers {user_id, speaker} POST = register a person · DELETE = unregister (memories keep, tag drops) - /api/v1/memory/speakers (GET ?user_id=) -> {speakers:[{speaker, memories}], count, limit} - /api/v1/engram (GET) -> {engrams:[{name,description}], forms:[...]} - /api/v1/engram/run {name, user_id, query, form?, tz?} -> {engram, hops, count, memories, usage} - /api/v1/models (GET, no auth) -> {models:[{id,name,available,memory}]} - /health (GET, no auth) ## Engrams (built-in multi-hop retrieval pipelines - no LLM; Tablet 1 and up) - deep_recall - search, then expand around the top match. Thorough recall. - timeline - time-ordered by when events happened. For "when did X" / history. - gather - broad: expand around the top few matches to pull in everything related. - equilibrium - drift correction. A long session narrows retrieval to whatever the current turn resembles; this re-widens it across time, association and substance. For replies that loop or flatten - not for a factual lookup. - tone_stabilizer - the agent's own past words, widened away from the recent stretch, so it can return to its usual register. Needs assistant turns stored (speaker "me"). Input is billed per engine hop an engram runs, output by what it returns. Delivery forms (archive / memoir - time-aware rendering) need Scroll 1.2 and up: pass `form: "memoir"` or `form: "archive"` on any call (HTTP: the X-WOS-Form header) and every memory in that response carries a rendered `time` field. ## Caching (optional, /search only - repeated queries get cheap) Add `cache_control: {"ttl": "5m"}` or `{"ttl": "1h"}` to the body (SDKs: pass it on search). First call writes the cache (billed 2x the query tokens for 5m, 3x for 1h); each hit within the TTL bills 0.1x. Any write to that store invalidates its cache instantly - never stale. ## Filters (optional, /search only - narrow the search to part of a store) Add `filters` to the body. Applied BEFORE ranking, so you get the best matches WITHIN the filter, not a filtered top-N. Keys: categories (string[]) · event_from / event_to (when the content HAPPENED - metadata.event_date; RFC3339 or plain YYYY-MM-DD) · time_from / time_to (ingestion time) · min_importance (0-1). Unlisted keys are dropped, not rejected, so a typo silently widens the search. Retrieval stays purely semantic, so filtering behaves the same in every language. {"user_id":"alice","query":"what did we decide","filters":{"categories":["work"],"event_from":"2026-01-01"}} ## Idempotency (optional, writes only - make YOUR retry safe to repeat) Send an `Idempotency-Key` header on store / store-turn / bulk-store / supersede. The first response is replayed instead of storing again (10 minutes); the same key with a DIFFERENT body answers 422. Only 2xx are cached, so a failed call is retryable at once. Use it when the retry is yours - a job that died and was re-run, a queue that redelivers. Derive the key from the thing being stored ("import:row-42"), never a constant: one key reused for two different writes replays the first and the second is silently lost. Format: 1-128 chars of [A-Za-z0-9._:-]. The window is in-memory, so a deploy clears it early - it guards against a retry storm, it is not a durable ledger. SDKs: `idempotency_key=` / `{idempotencyKey}` / `add_idempotent(...)`. ## Speakers (optional - WHO said each memory, then recall by person) Explicit, like stores: register a person once, then store under their name. - Register: POST /api/v1/memory/speakers {user_id, speaker} (idempotent; up to 50 per store to start). - Store: metadata `{"speaker": "Bob"}` for a REGISTERED name, or `{"speaker": "me"}` for the agent's OWN words (reserved, lowercase, never registered or counted). Unregistered name -> 400, stores nothing. - Search: pass `speaker: "Bob"` to recall only that person (unregistered filter -> 404). Every returned memory carries a `speaker` field. Branch on status codes and error fields (e.g. speaker_limit: 50), never on message text. ## SDKs (Python · TypeScript · Rust - same memory surface, same version, released in lockstep; currently 2.2.24) For an app YOU are writing: your code decides exactly when to store and what to recall - deterministic, typed, automatic retries built in. pip install wontopos · npm install wontopos · cargo add wontopos Methods: add (store) · add_turn · add_bulk · search · recall · engram · update (supersede) · add_speaker · list_speakers · remove_speaker · history · stats · get · list_memories · iter_memories · export_memories · ping · delete · delete_all · create_store · list_stores · delete_store · list_models · list_engrams · with_model. Typed errors (catch the specific failure): RateLimitError · AuthenticationError · PaymentRequiredError · NotFoundError · PermissionDeniedError · BadRequestError · ConflictError · ServerError · APIConnectionError (all subclass WosError). client.rate_limit exposes the last call's remaining quota. Python also ships an async client: pip install "wontopos[async]" -> from wontopos import AsyncClient. ## MCP (beta - plug WOS memory into AI tools you did NOT build; currently 1.0.8) For finished tools (Claude Code, Claude Desktop, Cursor, VS Code, Windsurf, Gemini CLI): the agent gets nine memory tools and decides on its own when to use them, guided by the tool descriptions. Zero integration code. Same API and same stores as the SDKs. claude mcp add wontopos --env WONTOPOS_API_KEY=wos-live-... -- npx -y wontopos-mcp (add --env WONTOPOS_USER_ID=my-project to pick which store it remembers into) Tools: recall · remember · search · update (supersede a changed fact) · forget · list_memories (browse + housekeeping) · engram (any engram the service offers - see Engrams above) · stats · create_store. The agent doesn't just consume memory - it curates: noticing the user correct a fact -> update; skimming for stale entries -> list_memories. Env: WONTOPOS_USER_ID (which store; unset = the seeded `default`, and the server says so on stderr) · WONTOPOS_MODEL (default tablet-1; set scroll-1.2 to opt into the fuller, pricier engine) · WONTOPOS_BASE_URL (self-hosted) · WONTOPOS_READ_ONLY=1 (read tools only) · WONTOPOS_ALLOW_STORE_OVERRIDE=1 (multi-store; default is pinned). Security: pinned to ONE store by default (tools take no user_id argument unless you opt in), tool results capped so an oversized memory can't blow up the model's context, and inputs validated before any request. Use a DEDICATED key for MCP (keys carry their workspace, so it scopes what tools can touch), keep host tool-confirmation on for forget, and treat recalled memories as data - the tool descriptions already tell the agent this. ChatGPT reaches the same memory via Actions + the OpenAPI spec (its MCP connectors are remote-only; WOS does not offer a remote server yet). Official MCP registry: com.wontopos/mcp. SDK or MCP? Building a product -> SDK. Giving a finished AI tool a memory -> MCP. Same account underneath: what one surface stores, the other recalls. Memory belongs to the ACCOUNT, not the tool: the same store id written from ChatGPT (Actions + the OpenAPI spec above) recalls in Claude Code and your own agents, and back. ## Common patterns - Agent memory loop: recall(query) -> put `context` in the prompt -> after the reply, add_turn(user_msg, assistant_msg) to remember the exchange. - One store per end-user: use your app's user id as the WOS user_id. Memories never cross stores. - A fact changed: update(old_memory_id, new_content) instead of storing a contradiction. - GDPR erase: forget {user_id} with no memory_id wipes that user entirely. - Thorough recall: run deep_recall or gather instead of a single search; a repeated read-heavy query: add cache_control. - Pick a model per call: header `X-WOS-Model: scroll-1.2`, or SDK with_model("scroll-1.2"). - New store 404s? Create it first (POST /memory/collection) or write to "default". ## Models & pricing (usage-only, per 1M tokens in/out; detail pages: wontopos.com/model/) - tablet-1 $2 / $3 - lean and fast, ~1,200 tokens/query, 85.2% LongMemEval-S. Live. - scroll-1 $4 / $8 - fuller context ~3,700 tokens/query, ~90.7%; adds an LLM query-understanding layer over the SAME stored memories. Live. - scroll-1.2 $4 / $8 - the current Scroll: sentence-level recall over the same memories, ~2,800 tokens/query, 92.3% (5-run mean, σ 0.4); supports delivery forms (memoir/archive). Live. - book not priced yet - a different design, in development. (Benchmark: 500 questions, 5-run mean; per-model readers named in each report, judge GPT-4o.) Plus a flat $0.0001 per request. Storage free, no memory caps, same rate at any volume. Prepaid via Stripe (top up from $5), metered from the first call. BYOK: your own LLM keys bill at your provider, never at WOS. Usage tiers auto-raise the monthly spend cap with cumulative top-ups ($5 -> $100/mo, $40 -> $500, $200 -> $1,000, $400 -> $5,000, $1,000 -> $25,000). Tier 6 is enterprise: custom caps, SSO/SAML, 99.9% SLA, a dedicated region or self-hosting. ## Errors & limits 400 bad request · 401 invalid API key · 403 the model can't do that (e.g. a form on Tablet) · 404 not found (store or filter target) · 429 rate limited (honors Retry-After; the SDKs auto-retry 429 always, 502/503 on reads only - a retried write could double-store) · 5xx server error. Error bodies carry {error: {type, message, request_id}} - quote request_id when contacting support. Rate limit ~120 requests/min (scales with your tier) · request body cap 10 MB. ## Trust - Legal (Terms · Privacy · DPA with SCCs · AUP · SLA): https://wontopos.com/legal - review before shipping to end-users. WOS is a data processor: you own the data; we never train on it, view it, or sell it. Right-to-erasure is one call (forget with no memory_id). - Live status, uptime, incidents: https://status.wontopos.com (99.3% SLA; 99.9% on Tier 6). - Durability: snapshots every 6 hours, kept 7 days locally and copied off-site. - We are early and the API can change between releases: pin your SDK version and re-check this file (updated with each release) before you upgrade. - Closed-source engine: it uses embeddings; the embedding model, vendor, and ranking internals are not public. Describe behavior, not implementation. - No LLM ever runs over your stored memories, on any model. Tablet runs no LLM at all; Scroll may use one to reformulate the QUERY only (never your stored data), via an LLM sub-processor (Google, Anthropic, or OpenAI).