twin memory

Quickstart

Add a message for one of your users, search it, and forget it. Four requests, no SDK needed.

SDKs

Python, TypeScript and Go SDKs are coming. Until then every example here is plain HTTP, so it works from any language today.

1. Create a key

In the console, open API keys and create a key with read and write access for your project. It is shown once. Keep it on your backend, and export it for the examples below. Authentication & keys says which key goes where.

export TWIN_MEMORY_API_KEY="tm_..."

2. Add a message

Every request names one of your end users with X-Twin-Account. Use whatever id your app already has for them. The first write creates that end user's memory.

curl https://api.twinmemory.dev/v1/memory/add \
  -H "Authorization: Bearer $TWIN_MEMORY_API_KEY" \
  -H "X-Twin-Account: user_42" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [{
      "id": "msg-001",
      "session": "onboarding",
      "role": "user",
      "text": "I just moved to Lisbon from Berlin for the new job at Acme."
    }]
  }'

The answer is 202 Accepted: the item is committed, and lands a moment later. Add "wait": true to the body to hold the request until it has landed, for up to 10 seconds.

{
  "items": [{ "id": "msg-001", "event_id": "ev_620270db0bff9328", "landed": false }],
  "queued": 1,
  "pending": 1
}

Send the same id again with new text and it is an edit, not a second message, so retrying a request is always safe. Edit only to fix the same statement; when something changes, add a new item, and memory keeps both, dated. See Items for every field.

curl https://api.twinmemory.dev/v1/memory/search \
  -H "Authorization: Bearer $TWIN_MEMORY_API_KEY" \
  -H "X-Twin-Account: user_42" \
  -H "Content-Type: application/json" \
  -d '{ "query": "Where does Alice live?" }'

Right after an add, search finds the message by its words and returns it under hits: saved text is searchable within seconds. Once the conversation has been read in the background, the same search also returns dated facts, and once it is folded, a known block. Reading takes a minute or two for the earlier part of a conversation, and the newest part is read once the conversation has been quiet for about 10 minutes. Folding follows at most about every 30 minutes. status tells you which step an item has reached: landed, then read, then folded. Records and reading has the details.

{
  "query": "Where does Alice live?",
  "found": 1,
  "known": ["- Alice Chen · home_city: Lisbon [said 2026-10-01]; Berlin [said 2024-06-01]"],
  "facts": [
    {
      "id": "th_8b5e5d4c8946d815#r028d461216652fe5",
      "text": "(happened 2026-10-01) Alice lives in Lisbon (Alice · home_city: Lisbon) (state)",
      "source": "passages/memory/messages/th_8b5e5d4c8946d815/p_0001.md"
    }
  ],
  "hits": [{ "id": "th_8b5e5d4c8946d815/p_0001", "text": "…I just moved to Lisbon from Berlin…" }]
}

The response above is shortened. found counts the passages under hits. Hand known, facts and hits to your model as context. Each carries the address it came from, so the answer can cite it.

4. Forget

curl https://api.twinmemory.dev/v1/memory/forget \
  -H "Authorization: Bearer $TWIN_MEMORY_API_KEY" \
  -H "X-Twin-Account: user_42" \
  -H "Content-Type: application/json" \
  -d '{ "items": ["msg-001"] }'

The message, the passages cut from it, the facts read from it, its versions and their index entries are gone, and the response counts each table before and after. Keep it as your record of the erasure. See Erasure & receipts.

Next

  • Read the search reference for scopes, date bounds and every response field.
  • Build a chat assistant that searches before each reply.
  • Give Claude Code, Cursor or VS Code the same memory over MCP.

On this page