MemorySync
API Reference

Conversation History & State

Keep the transcripts and framework state your code reads back, apart from memories. Nothing written here is ever listed, searched, recalled or counted as a memory.

Two stores beside memories

Some integrations need exact data back: a chat history a framework replays, or key/value state it reloads between runs. Those live here, in conversation history and framework state, addressed by tenant_id and user_id in the body like the other /v1 operations. Memories stay the durable facts: history appends and state puts can send text to fact extraction, and the facts found become memories of the same user.

OperationMethod and pathBilling
Append turns to a sessionPOST /v1/history/appendOne add per user turn sent to extraction, or one add when it stores new turns and extracts none. A retry that stores nothing new is free.
Read a session’s turnsPOST /v1/history/listOne retrieval
Delete a session or turnsPOST /v1/history/deleteFree
Store a key’s valuePOST /v1/state/putOne add for a new value. Putting the value a key already holds is free.
Read one valuePOST /v1/state/getOne retrieval
List values under a namespacePOST /v1/state/listOne retrieval
Rank values against a queryPOST /v1/state/searchOne retrieval
Delete keys or a namespacePOST /v1/state/deleteFree
List namespacesPOST /v1/state/namespacesOne retrieval
  • Every body carries tenant_id and user_id.
  • source (default api, up to 64 characters, lowercased) scopes the rows. Pass your integration’s name.
  • Rows come back with h_<n> ids. The delete routes and DELETE /memory/forget also accept m_<n> or the bare number.

Append History

POST/v1/history/append
201 Created

Append 1 to 100 turns to one session, in order. Turns are stored exactly as given and read back with List History. With extract: true (the default) the user turns are also sent to fact extraction.

FieldTypeContract
session_idstringRequired, 1–2000 characters. The conversation the turns belong to.
turnsobject[]Required, 1–100 turns. Each has role (user, assistant, system, tool or any label up to 64 characters; human is stored as user, ai as assistant) and content (string, may be empty), and optionally seq (integer ≥ 0, orders the transcript), occurred_at, payload (JSON your framework needs to rebuild the message) and idempotency_key (up to 200 characters).
sourcestringOptional. Scopes the rows. Defaults to api.
extractbooleanOptional. Also extract durable facts from the user turns. Defaults to true.
metadataobjectOptional. Scalar values are copied onto the extracted facts.
  • A turn is recognised as a retry when it carries the same idempotency_key, or the same seq, role and content. The stored turn comes back with created: false instead of being added twice.
  • A turn with neither an idempotency_key nor a seq is appended every time.
import os
from memorysync import MemorySyncClient
client = MemorySyncClient(
api_key=os.environ["MEMORYSYNC_API_KEY"],
base_url="https://api.memorysync.io",
project_id=os.environ["MEMORYSYNC_PROJECT_ID"],
end_user_id="usr_7f3a9c2e",
)
result = client.append_history(
tenant_id="acme",
user_id="usr_7f3a9c2e",
session_id="thread-42",
source="my-app",
turns=[
{"role": "user", "content": "I always fly with the window seat.", "seq": 0},
{"role": "assistant", "content": "Noted, window seat it is.", "seq": 1},
],
)
response.json
{"items":[{"id":"h_1000000000123","session_id":"thread-42","role":"user","content":"I always fly with the window seat.","seq":0,"occurred_at":null,"payload":null,"source":"my-app","created_at":"2026-09-27T10:00:00","created":true},{"id":"h_1000000000124","session_id":"thread-42","role":"assistant","content":"Noted, window seat it is.","seq":1,"occurred_at":null,"payload":null,"source":"my-app","created_at":"2026-09-27T10:00:00","created":true}],"request_ids":["9f3a51c0e2d84b7a"],"status":"ok"}

List History

POST/v1/history/list
200 OK

A session’s turns, oldest first. When any turn in the session has a seq, the turns are ordered by it.

FieldTypeContract
session_idstringRequired. The session to read.
sourcestringOptional. Omit to read turns from every source.
limitintegerOptional. 0 (the default) returns every turn, otherwise the latest N, at most 5000.
import os
from memorysync import MemorySyncClient
client = MemorySyncClient(
api_key=os.environ["MEMORYSYNC_API_KEY"],
base_url="https://api.memorysync.io",
project_id=os.environ["MEMORYSYNC_PROJECT_ID"],
end_user_id="usr_7f3a9c2e",
)
history = client.list_history(
tenant_id="acme",
user_id="usr_7f3a9c2e",
session_id="thread-42",
source="my-app",
)
for turn in history["items"]:
print(turn["role"], turn["content"])
response.json
{"items":[{"id":"h_1000000000123","session_id":"thread-42","role":"user","content":"I always fly with the window seat.","seq":0,"occurred_at":null,"payload":null,"source":"my-app","created_at":"2026-09-27T10:00:00"}],"total":1}

Delete History

POST/v1/history/delete
200 OK

Delete a whole session or specific turns. Not billed.

FieldTypeContract
session_idstringDelete this session. Exactly one of session_id or ids.
idsarrayDelete these turns (h_<n> strings or numbers, up to 1000). Exactly one of session_id or ids.
sourcestringOptional. Limit the delete to one source.
delete_derived_factsbooleanOptional. Also delete the memories extracted from the deleted turns. Defaults to false.
response.json
{"deleted_ids":["h_1000000000123","h_1000000000124"],"deleted_fact_ids":["m_311"]}

Put State

POST/v1/state/put
201 Created

Store the value of a key in a namespace. One key holds one live value: a put replaces the previous one. Putting the identical value again returns created: false, so a retry is safe.

FieldTypeContract
namespacestring[]Required, 1–20 segments, for example ["memories", "user-1"].
keystringRequired, 1–1000 characters.
valueobjectRequired JSON object.
textstringOptional searchable text. Defaults to the value’s JSON.
sourcestringOptional. Scopes the rows. Defaults to api.
indexbooleanOptional. Embed text so Search State can rank it by meaning. Defaults to true.
extractbooleanOptional. Also extract durable facts from text, like Add Memory. Defaults to false.
replace_derived_factsbooleanOptional. When the put replaces a value, also delete the facts extracted from the previous one. Defaults to false.
metadataobjectOptional. Scalar values are copied onto the extracted facts.
import os
from memorysync import MemorySyncClient
client = MemorySyncClient(
api_key=os.environ["MEMORYSYNC_API_KEY"],
base_url="https://api.memorysync.io",
project_id=os.environ["MEMORYSYNC_PROJECT_ID"],
end_user_id="usr_7f3a9c2e",
)
result = client.put_state(
tenant_id="acme",
user_id="usr_7f3a9c2e",
namespace=["preferences"],
key="seat",
value={"seat": "window"},
text="Prefers the window seat.",
source="my-app",
)
response.json
{"item":{"id":"h_1000000000130","namespace":["preferences"],"key":"seat","value":{"seat":"window"},"text":"Prefers the window seat.","source":"my-app","created_at":"2026-09-27T10:00:00","updated_at":"2026-09-27T10:00:00"},"created":true,"request_id":null,"deleted_fact_ids":[],"status":"ok"}

Get and List State

Read one key
POST/v1/state/get
200 OK
Read every key under a prefix
POST/v1/state/list
200 OK

Get returns the current value of one key (namespace, key, source) as {"item": …}, with item: null when the key has none. List returns the current value of every key whose namespace equals namespace_prefix or starts with it, segment by segment, newest first, as {"items": […], "total": n}; limit is 1–5000 (default 100) and offset pages. Both read the source the values were put with (default api).

POST/v1/state/search
200 OK

Rank the current values under namespace_prefix against query. Each item carries a score: cosine similarity when the value was embedded (index: true), token overlap otherwise. limit is 1–100 (default 10). Filtering on value fields is left to the caller.

import os
from memorysync import MemorySyncClient
client = MemorySyncClient(
api_key=os.environ["MEMORYSYNC_API_KEY"],
base_url="https://api.memorysync.io",
project_id=os.environ["MEMORYSYNC_PROJECT_ID"],
end_user_id="usr_7f3a9c2e",
)
hits = client.search_state(
tenant_id="acme",
user_id="usr_7f3a9c2e",
query="seating",
namespace_prefix=["preferences"],
source="my-app",
)

Delete State and List Namespaces

Delete keys or a namespace
POST/v1/state/delete
200 OK
List the namespaces in use
POST/v1/state/namespaces
200 OK

Delete takes a namespace and keys (up to 1000; omit keys to delete the whole namespace, an empty list deletes nothing) plus delete_derived_facts to also delete the memories extracted from the deleted values, and answers {"deleted_ids": […], "deleted_fact_ids": […]}. It is not billed. Namespaces lists the namespaces holding at least one value for this user, as {"namespaces": [["a", "b"], …]}; prefix keeps those starting with the given segments and max_depth cuts each to that many segments.

Quota behaviour

Errors and next action

A 401 or 403 means the key is missing, invalid, or lacks the scope for the operation. A 422 names the invalid field in detail — for example more than 100 turns, both session_id and ids on a delete, or a namespace with more than 20 segments. For 429 or 5xx, retry with the same payload: idempotency keys, seq and identical puts make the retry safe.

Safety notes

Was this page helpful?