Memory
Let chat recall stored facts and capture new ones. Memory is on by default for signed-in keys and off for developer keys until a request opts in.
Memory gives chat two independent abilities, each set per request in the
memory object. Recall reads the stored memories most relevant to the turn
into it as background context. Capture lets the gateway store a durable
fact from the turn for later ones. When a request sends no memory object, the
key decides whether each one runs.
Defaults depend on the key
| Key | Recall and capture when memory is omitted | bucket in GET /api/v1/limits |
|---|---|---|
| Signed-in credential: device login, OAuth login, and the Cruxy web app, desktop, CLI, and mobile | On | subscription |
| Developer API key, created in the Cruxy Console | Off | apikey |
| Headless key | Off | headless |
A key minted by device login or OAuth login is a signed-in credential whichever client ran the flow, a third-party tool included. A developer key's memory is off because the key often serves people other than its owner: recall would put the owner's stored memory into their turns, and capture would store what they say. Use a developer API key for a server; see Get your API key.
Opt in from a developer key
Send true for each side you want. This request comes from an internal tool
that only the key's owner uses, so it recalls the owner's memory and lets the
turn store a new fact:
curl https://api.cruxy.ai/api/v1/chat \
-H "Authorization: Bearer $CRUXY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "vaani",
"stream": false,
"memory": { "recall": true, "capture": true },
"messages": [
{ "role": "user", "content": "I work in IST. When should our weekly sync with the London team be?" }
]
}'const res = await fetch('https://api.cruxy.ai/api/v1/chat', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CRUXY_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'vaani',
stream: false,
memory: { recall: true, capture: true },
messages: [
{
role: 'user',
content: 'I work in IST. When should our weekly sync with the London team be?',
},
],
}),
})
const completion = await res.json()
console.log(completion.message.content)
console.log(completion.cruxy.memory_recall) // what was recalled, or undefined
console.log(completion.cruxy.memory_capture) // what was stored, or undefinedtrue needs a key whose allowed scopes include the memory family. An
unscoped key has every family. A key without it gets 403
key_scope_forbidden, refused before anything is billed:
| Error field | Value |
|---|---|
code | key_scope_forbidden |
required_scope | memory |
allowed_scopes | The key's own families, for example ["chat"] |
A 403 never clears by waiting. A key whose allowed scopes omit memory can
never use memory through chat, which makes it the key to issue for a service
that must never see anyone's memory.
What recall adds
Recall draws on the same resolved view GET /api/v1/memories lists: your
personal memory, your organization's, and every vault you can read whose
memory_scope is default.
vault_idnames the vault a chat is inside. Inside avault_onlyvault, recall draws on that vault's memory alone, without personal or organization memory. Avault_idyou cannot read is treated as if you sent none, never reported.mentioned_vault_idsadds up to three vaults the turn references, such as an@VersionGateyour picker resolved. Mentions share the turn's recall budget rather than enlarging it, and never make a vault writable.- Billing. The recalled block is billed input, so input tokens fall by its size when recall is off.
The response reports what was recalled in cruxy.memory_recall: the scope and
key of each memory, and for a vault memory its vault_id (and vault_name
when it resolves). It never includes the stored value, and the field is absent
when nothing was recalled.
{
"cruxy": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"routed_tier": "vaani",
"reasoning_effort": "low",
"memory_recall": {
"count": 1,
"items": [{ "scope": "personal", "key": "timezone" }]
}
}
}What capture stores
After the turn is answered, capture decides whether the exchange held a durable fact, such as a stated preference, a stable setting, or a constraint about your work, and stores it.
- Where it goes. Your personal memory, or the vault the request's
vault_idnames when you can write to it. A vault you can only read demotes the capture to your personal memory. Capture never writes organization-wide memory, whatever your role. - What it drops. Content carrying an email address, a phone number, or a card number is dropped before any write.
- What it reports. Every capture appears in
cruxy.memory_capture, with the scope and key of each memory stored and never the value. No field means nothing was stored. - What it never does. Capture cannot fail or delay a turn. If extraction is unavailable, it stores nothing.
On a stream, memory_recall rides the role primer and memory_capture rides
the terminal usage frame, because what a turn stored is known only once the
answer is complete.
Turn memory off for one request
Any key may send false for either side, and false needs no scope:
{ "memory": { "recall": false, "capture": false } }recall: false reads no memory and ignores mentioned_vault_ids.
capture: false stores nothing. The two are independent, so
{ "capture": false } alone keeps the turn personalized by what is already
stored while adding nothing new.
Pin Cruxy-Version: 2026-09-26 on every request that relies on false. A
gateway that predates the field would ignore it and capture anyway; with the
pin, such a gateway refuses the request with 400 unknown_version instead.
A server should use a developer API key. If a signed-in credential does serve
other people, every request must send both false values, and rules: false,
or the owner's memory and rules reach their turns.
Review and remove what is stored
GET /api/v1/memorieslists your effective memory, one entry per key, with theidof the row that won.DELETE /api/v1/memories/{id}removes that entry. You can delete what you can write: your own memories, a vault's where your membership grantscan_write, and organization memory if you are an owner or admin.PUT /api/v1/memoriesstores a value by hand. A key or value carrying an email address, a phone number, or a card number is refused with400pii_rejected, and nothing is stored.
API Reference has every field of the memories endpoints.
Next steps
- Standing rules: the other per-request context, with the same default split.
- Get your API key: the two kinds of key and their restrictions.
- Streaming: where the
cruxysignals ride on a stream. - Errors and retries: handle
403key_scope_forbidden.