Skip to main content

Agent Memory

With memory on, an agent remembers facts about each of your customers from one conversation to the next. Examples are their name, preferred language, an open order or a callback it promised. When the same customer comes back on any channel the agent knows them by, it picks up where it left off, without asking for everything again.

Memory is off by default. While it is off, nothing is stored or recalled, and the agent's prompts are exactly what they are without the feature.

Turning memory on​

Memory needs two switches, and both must be on.

  1. Organization consent. An organization admin turns on Agent memory in the organization settings (PATCH /api/organizations/{org_id}/memory-consent with {"memory_consent": true}). This is your organization agreeing that the platform may store facts about your end customers on your behalf. If you turn it off, every agent in the organization stops recalling and storing memories at once. Memories already stored stay viewable in the editor until they expire or you delete them.
  2. The agent's toggle. Open the agent, go to the Memory tab and switch Remember customers on.

On the same tab you can set:

SettingDefaultWhat it does
Memories injected8How many of the customer's most important and most recent memories the agent sees at the start of a conversation. It can look up others mid-conversation.
Retention (days)180A memory is deleted this many days after it was last written. 0 deletes memories at the next nightly purge.
Extraction instructions(empty)Extra guidance for what to remember, for example "remember order numbers and preferred delivery slot".
Phone disclosureContextualContextual: on calls, the agent uses memories to personalize but does not volunteer personal details until the caller confirms who they are, because caller ID can be spoofed and phones can be shared. Trusted: the agent uses memories freely.
Accept unsigned widget user idsOffSee Website widget. Leave this off in production.

How a customer is recognized​

Memory is per customer and per agent. Two agents never share memories, and the facts of one customer are never shown for another.

ChannelWho the customer isNotes
Phone (Twilio, Plivo, SIP)The caller's number (the number called, for outbound calls)Withheld, anonymous or restricted caller IDs get no memory.
WhatsAppThe sender's number
Website widgetYour signed-in user's id, if you pass it (below). Otherwise an anonymous per-browser visitor idA visitor id is lost when the visitor clears site data or switches browser.
Chat APIThe end_user_id you send

The same person on two channels, for example a phone call and a widget login, counts as two customers.

Website widget: identify signed-in users​

Without any change, the widget remembers a visitor per browser. To recognize signed-in users on any device, pass your own user id and a signature your server computes. The signature stops anyone from impersonating another user by editing the id in their browser.

user_hash = hex(HMAC-SHA256(key = your widget identity secret, message = user_id))

The widget identity secret is shown on the agent's Memory tab. It belongs on your server only. Never put it in page source, a mobile app bundle or a public repository.

1. Compute user_hash on your server​

Node.js

import crypto from "node:crypto";

// VOIS_WIDGET_IDENTITY_SECRET comes from your server's secret store.
export function voisUserHash(userId) {
return crypto
.createHmac("sha256", process.env.VOIS_WIDGET_IDENTITY_SECRET)
.update(String(userId), "utf8")
.digest("hex");
}

// e.g. in your page handler
app.get("/support", requireLogin, (req, res) => {
res.render("support", {
voisUserId: req.user.id,
voisUserHash: voisUserHash(req.user.id),
});
});

Python

import hashlib
import hmac
import os


def vois_user_hash(user_id: str) -> str:
secret = os.environ["VOIS_WIDGET_IDENTITY_SECRET"].encode("utf-8")
return hmac.new(secret, str(user_id).encode("utf-8"), hashlib.sha256).hexdigest()


# e.g. Flask / FastAPI view
# return render_template("support.html",
# vois_user_id=user.id, vois_user_hash=vois_user_hash(user.id))

Use a stable, opaque id such as your database user id, not an email address or phone number. It may be up to 128 characters. The id is never stored as is: the platform stores only a one-way hash of it, plus a masked label (for example user•••12) that is shown in the editor.

2. Pass both values to the widget​

<script src="https://<your-vois-host>/chat-widget.js" data-public-key="vois_pk_..." async></script>
<script>
window.addEventListener("load", function () {
window.VoisChat.mount({
projectId: "<project id>",
agentId: "<agent id>",
channelId: "<channel id>",
publicKey: "vois_pk_...",
userId: "{{ voisUserId }}",
userHash: "{{ voisUserHash }}",
});
});
</script>

If you use the headless API, send the same values as user_id and user_hash in POST /widget/v1/session. For the realtime WebSocket, send them as query parameters. You can also send visitor_id, your own anonymous per-browser id, for visitors who are not signed in.

curl -X POST "https://<your-vois-host>/widget/v1/session" \
-H "X-API-Key: vois_pk_..." \
-H "Content-Type: application/json" \
-d '{"agent_id": 42, "user_id": "8812", "user_hash": "3b1f...e9"}'

What happens with each input:

  • A valid user_hash means the visitor is remembered as your user.
  • A missing or wrong user_hash means the user id is ignored, and the visitor is remembered per browser only. Accept unsigned widget user ids overrides this; use it only for testing.
  • No ids at all means no memory for that visitor.

Chat API​

Send end_user_id with each message in POST /api/agents/{agent_id}/chat:

{
"session_id": "order-help-2026-10-04-8812",
"message": "Any update on my refund?",
"end_user_id": "8812"
}

Use the same stable id each time for the same customer. The rules above apply here too: up to 128 characters, opaque, and stored only as a hash.

What is stored and what is not​

Stored, per customer and per agent:

  • Short facts in plain language, such as "Prefers Arabic.", "Has an open refund for order 5521." or "Asked for a callback after 5 pm."
  • A category (profile, preference, issue, commitment or other), an importance from 1 to 5, the conversation it came from, and timestamps.
  • A one-way, keyed hash of the customer's identifier and a masked label for the editor (+97150•••123, WhatsApp +97150•••123, user•••12, Visitor 3f2a•••). The raw phone number or user id is not stored with memories.

Never stored:

  • Payment card or bank details, government ID numbers, passwords, one-time codes or other credentials, and health information. The extraction step is told never to keep these. Every fact is also screened by a sensitive-data detector, and a flagged fact is discarded. If the detector itself is unavailable, the fact is discarded too.
  • Anything about one customer in another customer's memory.

Where memories come from:

  • After each conversation, the platform reads the transcript and keeps any durable facts. It merges a new fact with an existing one instead of piling up duplicates, and replaces facts that have become outdated. For WhatsApp and chat threads, which have no clear end, this runs once a thread has been idle for 30 minutes.
  • During a conversation, the agent can save a fact when the customer asks it to ("remember that I'm vegetarian") and look up older memories.
  • In the editor, your team can add, edit and delete memories by hand.

Retention​

Each memory expires Retention (days) after it was last written or updated. A nightly job permanently deletes expired memories, together with their search index entries. Lowering the retention re-dates the existing memories at once, so they are removed at the next nightly run. Deleting an agent deletes all its memories.

Memories follow the agent's own retention setting, not your organization's transcript and recording retention. A memory can therefore outlive the transcript it came from.

Forgetting​

  • The customer asks. If a customer says "forget that" or "forget everything about me", the agent removes those memories, or every memory it holds for them. It does this during the conversation and again after the conversation.
  • From the editor. On the agent's Memory tab, find the customer and choose Forget this customer, or delete single memories. To find someone, you can type their full phone number or user id; it is matched by hash.
  • By API. DELETE /api/agents/{agent_id}/memories/users/{end_user_key_hash} deletes every memory of one customer. DELETE /api/agents/{agent_id}/memories/{memory_id} deletes one memory.

Deletion is permanent: the rows and their search index entries are removed, not hidden. If removing a search entry fails, the nightly job retries it.

Billing​

Memory uses the same metered usage as the rest of the agent:

  • Extracting and merging memories uses the platform's classifier model. These tokens are billed on the LLM tokens line, in the organization and project of the agent. They are billed even for voice calls charged per minute, because they come on top of the conversation.
  • Indexing and searching memories uses the embedding model. These tokens are billed with embedding usage on the Knowledge base line.

There is no separate memory fee and no storage charge for memories.