API reference
30 endpoints. Base URL https://api.wontopos.com, JSON in and JSON out, one
API key in an X-API-Key header. Every call is scoped to a store — the user_id that holds one end-user's memories.
Two things authenticate a call. The API key says which workspace you work in, and user_id says whose memories you are touching. Stores are explicit: reading or
writing a store that was never created returns a 404 rather than inventing one. Start with Create a store.
Writing memories
Put something in, correct it later, or take it back out.
- POST /api/v1/memory/store Store one memory
- POST /api/v1/memory/store-turn Store a conversation turn One user + assistant exchange into short-term and long-term memory.
- POST /api/v1/memory/bulk-store Bulk-ingest a large blob Split and indexed server-side. For backfilling long histories.
- POST /api/v1/memory/supersede Supersede an out-of-date memory Replaces old content with new; the old memory stops answering. Speaker tags follow.
- POST /api/v1/memory/forget Delete one memory — or every memory in the store With memory_id: delete that memory. WITHOUT memory_id: delete EVERY memory in the store (GDPR erase). Omitting the id is destructive by design — send it explicitly for single deletes.
Reading memories
Semantic search, one-call context, and plain reads that return the original text.
- POST /api/v1/memory/search Search memories Returns
memories, ordered most useful first.imagescan appear beside it on a model that stores images (one by default;max_imagessets 0 to 5),self_memorieson a model that keeps the agent's own words apart, andverify_usedwhen you passverifyabove 0: the number of extra passes that added memories; each is billed for what it added. In the SDKs,search()returns one list: the memories, then the agent's own words, then any image rows, with no id repeated. It does not carryverify_used. Usesearch_full()(searchFull()in TypeScript) to keep those fields apart. - POST /api/v1/memory/recall One-call LLM context Short-term turns + long-term matches + surrounding context in one bounded call.
- POST /api/v1/memory/get Fetch one memory by id (the original text you stored) Returns a single stored memory by the id that /store or /list returned: the original text plus its metadata, and nothing internal. Tablet 2, the default model, answers with the memory itself; older models wrap it as
{user_id, memory: {...}}. The SDKs return the memory either way. An unknown id, or one from another store, is a 404. - POST /api/v1/memory/list List a store’s memories (the original text you stored) Paginates the raw memories in a store — the text you stored plus its metadata, and nothing internal. Use it to browse or export a store. Pass the returned
next_cursorback ascursorfor the next page; a null cursor means the last page. - POST /api/v1/memory/history Recent turns (short-term memory)
- POST /api/v1/memory/stats Memory counts for a store
Images
An image can be the memory itself, found by a sentence in any language.
- POST /api/v1/memory/image The picture we hold for an image memory The only call on this plane that answers with something other than JSON: the stored image itself, with its own Content-Type. Not necessarily your upload — anything over 1568px on its long edge (2500px at most) was stored downscaled, and re-encoded to WebP unless it was a JPEG or the re-encode came out bigger, so a PNG usually comes back as image/webp. When the format changed,
x-wos-image-converted-fromnames what you sent; take the file extension from Content-Type rather than from your upload. A memory that exists but carries no image is a 404, same as an unknown id. - POST /api/v1/memory/images A store’s image memories, newest first Cursor paging, never an offset: hand
next_beforeandnext_skip_idsback asbeforeandskip_ids. A bulk write stamps many memories with the SAME instant, so cutting on time alone silently drops the rest of that instant.countis the store total, so you can show “142 images” without walking every page. - DELETE /api/v1/memory/image Remove the image, keep the text Drops the image and leaves the caption — unless the memory was only a image, in which case the memory goes too. The response says which happened.
preview: trueasks without changing anything.
Stores
Each end-user gets their own store. Stores are explicit: writing to one that does not exist is a 404.
- POST /api/v1/memory/collection Create a store Stores are explicit: a store must exist before you write to or read it. Idempotent. Every account starts with a built-in "default" store.
- GET /api/v1/memory/collections List your stores
- DELETE /api/v1/memory/collection Delete a store and ALL its memories Destructive. Also drops the store’s registered speakers and their tags.
Speakers
Who said each memory, so a group conversation does not collapse into one voice.
- POST /api/v1/memory/speakers Register a person Speakers are explicit, like stores: register once, then store with metadata.speaker. "me" (the agent itself) is reserved, exact lowercase, never registered and never counted. Up to 50 people per store to start.
- GET /api/v1/memory/speakers List registered people
- DELETE /api/v1/memory/speakers Unregister a person Their memories stay, and the name can no longer tag a new memory or filter a search. On tablet-2 the tag stays on memories already stored with it.
- POST /api/v1/memory/by-speaker What one person said, newest first
speakeris the tag written at store time —"me"for the assistant’s own words, otherwise a registered name. Same cursor paging as /images.chunksandrecords_to_deletesay how much a delete would actually remove, which is usually more thanreturned— worth showing someone before they confirm one. (points_to_deleteis the same number under a deprecated name.)
Engrams
Multi-hop retrieval pipelines the engine runs for you, with no LLM in the loop.
- GET /api/v1/engram List engrams the selected model can run
- POST /api/v1/engram/run Run an engram Built-in multi-hop retrieval in one call. Call GET /api/v1/engram for the catalog the selected model can run, with a description of each. Available: deep_recall, timeline, equilibrium, tone_stabilizer, gather. Input is billed per engine hop the engram performs, output by what it returns.
Won — calls for the model
Free, rate-limited calls meant for the model to ask on its own, not for your UI.
- GET /api/v1/won/usage What this key has spent, and what is left Answers "can I keep going?". Returns this API key's lifetime spend, its workspace's spend over the last
daysdays, and the prepaid balance that gates the next call. Priced through the same path as the balance itself, so the figure here and the amount actually charged cannot disagree. Carries NO charge and skips the balance gate — an account at zero must still be able to find out why. Rate limited to 10 requests per minute. - GET /api/v1/memory/usage What this key has spent (alias of /api/v1/won/usage) Identical behaviour and rate-limit budget — see /api/v1/won/usage.
- POST /api/v1/won/revisions How much of this store has been altered How many memories in this store have been changed since they were written. Intended to be asked freely — by your app and by a model reading its own store — so it carries NO charge: no tokens, no per-request fee. Rate limited to 10 requests per minute, counted per endpoint per account, separately from your paid limit. Counts transforms (supersede); does not count deletions — a deleted memory leaves nothing to count — and counts only the memories you stored, not anything the engine keeps for itself. By default the answer is COUNTS ONLY, and its size does not depend on how much memory you have: a store of a hundred and a store of a hundred million get the same few numbers back. Send
includeto also receive one page of the memories behind a number — at most 20 per call, and never both lists at once. Page withbefore/skip_idsfrom the previous response, as /api/v1/memory/images does. Also served at /api/v1/memory/revisions, which is where clients published before 2026-08-18 call it; the two share one rate-limit budget. - POST /api/v1/memory/revisions How much of this store has been altered (alias of /api/v1/won/revisions) The original path for this call, kept for good: wontopos-mcp 1.0.14 and every client published before 2026-08-18 call it here. Identical behaviour, identical rate-limit budget — see /api/v1/won/revisions for the full description.
- POST /api/v1/memory/lineage The edits that followed one memory, oldest first /won/revisions says how much a store moved; this says what happened to one fact. The chain runs forward from the id you pass: pass the first version's id to see the whole history; the current version's id comes back alone.
is_currentmarks the version in force.
Service
What is live and what this account can reach.
Errors
Errors come back with the HTTP status and a JSON body carrying a machine-readable code and a sentence a human can act on. The rules the whole API follows:
- Out of range is refused, not clamped. Asking for 20 results and silently getting 10
reads as "that is all there is", which is a worse failure than an error. The one exception
is the
dayswindow on usage, which the service clamps to 1-365; the SDKs refuse it. - A missing store is a 404, not an empty result. A cold store must never look like an empty one.
- 401 means the key is missing or wrong. 429 means you hit the rate limit for your tier.
The full status list, rate limits per tier, and retry guidance are in Errors & limits.
For coding agents
The whole API fits in one file at llms.txt — drop it into an IDE or a
coding agent and build. The machine-readable schema is at openapi.json (OpenAPI 3.1), and the same
surface is available over MCP with npx -y wontopos-mcp.