Guides / Build well

Privacy, consent and forgetting

Memory means storing what people tell your product, so privacy is part of the design, not an afterthought. This guide is for developers who decide what goes into Recall: what to keep out, what to tell users, how to opt someone out of being modelled, where the data goes, and how to forget a fact, a person or a whole customer.

Practical guidance, not legal advice. For your users' data you are the controller and Recall is your processor. Our commitments are in the privacy policy, the data processing agreement and on the security page; check your own obligations with your own counsel.

What to store, and what not

Recall learns from whatever you send it, and it does not detect or remove sensitive data for you. Decide before you send.

DataSend it?How
The conversation about what your product does: preferences, plans, history with youYesThis is what memory is for.
Passwords, API keys, one-time codes, card numbers, bank detailsNoStrip them before calling Recall. Once stored they appear in context, search and exports, and no answer needs them.
Identity numbers, exact addresses, anything you would not show a support agentOnly if your product needs itKeep it in your own systems and send a reference instead.
Health, religion, sexual orientation, political views and other special categoriesOnly with explicit consentAsk for consent first, record it, and check whether you need a data protection impact assessment. A coaching or health product is the typical case: see Health, fitness and coaching.
Other people mentioned in a conversationUnavoidableFacts are learned about the person speaking, but they can mention others. See what stays when you forget someone.
Your own ids, plan, channelYes, as metadataRecall stores and filters on metadata but does not learn from it. Never put secrets there.

Files you upload with uploadFile become messages, so the same rules apply to them.

Telling users

People should know that your product remembers them, and how to make it forget. Say in your privacy notice that conversations are stored to give your product memory, that an AI memory service and the AI model providers it uses process them, how long you keep them, and how to see and delete what is remembered. Something like this, adapted to your product and your own notice:

"We use Recall, an AI memory service, so the assistant remembers what you tell it across conversations. Your messages are stored in the EU (Frankfurt) and processed by AI model providers in the United States to build that memory; they are not used to train AI models. You can see and delete what the assistant remembers under Settings, Memory."

In the product itself, a "What I remember about you" page goes a long way: show the peer card and the facts (getCard and conclusions.list), and let people delete any of them. The access and forgetting examples below are the building blocks.

Opting a person out

The examples on this page follow a team assistant for one business customer: Sanne (user-8412) and a colleague (user-5120) talk to an assistant whose own lines Recall does not learn from.

import { Recall } from "@recall-memory/sdk";

const recall = new Recall({ workspaceId: "cust-1042" });

const sanne = recall.peer("user-8412");
const colleague = recall.peer("user-5120");
const assistant = recall.peer("assistant", { configuration: { observeMe: false } });
const standup = recall.session("chat-7731");
await standup.addMessages([
  sanne.message("I prefer short answers, and I work on the billing team."),
  colleague.message("I am covering the billing queue this week while Sanne is away."),
  assistant.message("Got it. Billing questions go to you this week."),
]);

Some people will use your product but not want to be profiled. Set observeMe: false on their peer, or on their membership in one session. From then on nothing is learned from their messages, by Recall or by any other peer. The messages are still stored and still appear in context, so the conversation keeps working.

// Someone who does not want to be modelled: their messages are still stored and shown in
// context, but nothing is learned from them, by Recall or by any other peer. It applies to
// new messages; facts already learned stay until you delete them.
await colleague.setConfiguration({ observeMe: false });
// Or for one conversation only, such as a call they did not agree to have analysed.
const call = recall.session("call-2291", {
  peers: { "user-8412": { observeMe: false }, assistant: {} },
});
await call.addMessages([sanne.message("Can you move my one-to-one to Friday?")]);

Least privilege

Keep account-wide keys on your own servers. Give each business customer's service a key scoped to its workspace, and code on a user's device a key scoped to that user's peer or session, never more. A peer or session key cannot delete a peer (it answers 401), so forgetting runs on your server. Keys are shown once and stored only as hashes; revoke them under API keys, and keys minted with POST /v3/keys are revoked together with the key that minted them. Designing workspaces, peers and sessions has a table of which key goes where.

Where the data goes

The database and the servers that run the API and dashboard are in Frankfurt (EU). To build memory and answer questions, text is processed by AI model providers in the United States, through OpenRouter, which we ask to use only model providers that do not collect the data they process. Customer data is not used to train models, and we do not use it for our own purposes: not to sell, not for advertising and not for profiling. Every connection uses HTTPS, and the database is encrypted at rest.

Sub-processorWhat it doesLocation
Vercel Inc.Runs the API, dashboard and background work, and serves the websiteFrankfurt, Germany (EU); company in the USA
NeonDatabase: stores all customer personal dataFrankfurt, Germany (EU); company in the USA
OpenRouter, Inc.Passes requests to AI models for text generation and embeddingsUSA
OpenAI (through OpenRouter)Runs the AI models that build memory and answer questions, and computes embeddingsUSA
Anthropic, PBC (through OpenRouter)Runs the AI models that build memory and answer questionsUSA

Transfers to the United States rely on the EU-US Data Privacy Framework where the recipient is certified, and otherwise on the European Commission's Standard Contractual Clauses. The DPA applies automatically when you accept the terms (a countersigned copy is available on request). It commits us to tell account owners at least 30 days before a sub-processor is added or replaced, and to notify you of a personal data breach without undue delay, within 48 hours at the latest. Resend, Stripe, Cloudflare and GitHub also work for us, but do not receive your customers' data. Full text: Data processing agreement.

Export and access requests

Settings, Export all data in the dashboard downloads everything your account stores as one JSON file, free and as often as you like (see Export your data). When one person asks for a copy of their data, read it through the API instead: what they said, and what Recall concluded about them. If an agent keeps its own view of them, include it too (agent.conclusionsOf(user).list()).

// A person asks for a copy of their data: what they said, and what Recall concluded.
// (waitForIdle is for this demo; real apps rarely wait for learning to finish.)
await recall.waitForIdle();
const said = [];
for (const session of await (await sanne.sessions()).toArray()) {
  const page = await session.messages({ filters: { peer_id: sanne.id } });
  said.push(...(await page.toArray()));
}
const copy = {
  said,
  facts: await (await sanne.conclusions.list()).toArray(),
  card: await sanne.getCard(),
};

If someone contacts us about data you sent, we forward the request to you rather than answering it ourselves, because you decide what happens to it.

Forgetting

To forgetCallWhat goesWhat stays
One factpeer.conclusions.delete(id), DELETE …/conclusions/{id}The fact, from the database at onceThe message it came from, the peer card (set it without that line), and earlier versions the night shift merged, which stay unused until the conversation or person is deleted
A conversationsession.delete(), DELETE …/sessions/{session}Its messages, summaries and the facts learned in it, in the background, usually within minutesPeer cards, and patterns the night shift drew across conversations
A personpeer.delete(), DELETE …/peers/{peer}Their messages and memberships, every fact and card about them or from their point of view, the summaries that quoted them (rebuilt without them) and cached answers, at onceOther people's messages, and facts about other people that mention them
A business customerDelete every session, then the workspaceEverything in the workspaceNone of its content

One fact. Show people what is remembered and delete what they pick. The peer card is kept separately, so update it too.

// "Forget that I'm on the billing team." Show people what Recall holds about them (a settings
// page works well) and delete what they pick.
const picked = copy.facts.find((fact) => fact.content.includes("billing team"));
if (picked) await sanne.conclusions.delete(picked.id);
// The peer card is kept separately: write it back without that line.
const card = copy.card ?? [];
const kept = card.filter((line) => !line.includes("billing team"));
if (kept.length < card.length) await sanne.setCard(kept);

A person. delete() needs a workspace or account key and is permanent. It returns what it removed; a 404 on a retry means the person is already gone. Then deal with what stays: facts about other people that name them, and other people's messages, which go only with their session.

// Erase the person: their messages and memberships, every fact and card about them or from
// their point of view, the summaries that quoted them, and cached answers.
const erased = await sanne.delete();
// Other people's messages stay, and so do facts about other people that mention her.
// Delete the ones that must go.
const facts = await (await colleague.conclusions.list()).toArray();
for (const fact of facts.filter((f) => f.content.includes("Sanne"))) {
  await colleague.conclusions.delete(fact.id);
}
// To remove what others said in that conversation too, delete the whole session.
await standup.delete();

Recall only deletes what it holds. Delete your own copies too: your database, logs, analytics, and anything you sent to your own model provider.

Retention

Recall keeps conversations until you delete them; nothing expires by itself. Choose a period that fits your product, write it in your notice, and enforce it with a scheduled job. Deleting old sessions removes their messages, summaries and the facts learned in them; peer cards and patterns drawn across conversations stay, so for people who have stopped using your product altogether, delete the peer.

// Your own retention rule, run daily: delete conversations older than 90 days. Their
// messages, summaries and the facts learned in them go too.
const cutoff = new Date(Date.now() - 90 * 24 * 60 * 60 * 1000).toISOString();
const old = await recall.sessions({ filters: { created_at: { lt: cutoff } } });
for (const session of await old.toArray()) await session.delete();

When a customer leaves

With one workspace per business customer (see workspaces), closing their account is two steps: delete the sessions, then the workspace. Deleting a workspace that still has sessions answers 409. Deleting your whole Recall account, under Settings, removes all customer data from our database at once.

// The customer closes their account: delete every session first, then the workspace
// (deleting a workspace that still has sessions answers 409).
for (const session of await (await recall.sessions()).toArray()) await session.delete();
await recall.deleteWorkspace();

Children

Recall itself is a service for businesses and developers, not meant for anyone under 16. If your product is used by children, the GDPR's rules on children's consent apply to you, and the age at which a child can consent differs between EU countries. In practice: get a parent's consent where the law requires it, store and learn only what the product needs (observeMe: false keeps a conversation as context without building a profile), keep retention short, and give parents the same access and deletion you give adults. Tutoring and education covers a learner product in detail.

Pitfalls

Next: Designing workspaces, peers and sessions · Cost and speed