Erase a person's data
This guide erases an end user's words when they ask you to forget them, and says what it clears, what it can't reach, and how it survives a backup restore.
When a person asks you to forget them, an erasure clears their words from the runtime: their memory facts, their conversations, the runs that served them, and what those runs left behind. It runs in the background.
Before you start
Section titled “Before you start”- Tenant admin, on a runtime with Kindgi 0.2's CLI. Only a tenant admin can start an erasure.
- Who to erase: the id your app gives the person, their
participantId, or one of the other selectors in Who an erasure can name. - The erasure ledger's key, set on the runtime, so the erasure survives a backup restore, as After a backup restore says.
Who an erasure can name
Section titled “Who an erasure can name”An erasure names exactly one of:
- an app's end user, with
--participant: theparticipantIdyour app gives on their conversations and facts; - an
externalsubject, with--external: a person facts name in theirsubjectswho never talked to an agent; - one conversation, with
--conversation; - one fact, every revision, with
--fact.
Erasing a Kindgi user, such as an employee of yours, isn't offered: what they
run is your organization's work, not their personal data. The API refuses it
with a 400.
Start one and follow it
Section titled “Start one and follow it”An erasure starts at once, and clears the person's words in the background:
pnpm exec kindgi memory erasures create --participant=end-7--participant=end-7 names the end user. create answers at once, with
202 and the erasure, and the work goes on in the background:
{ "id": "b8516687-01dc-4855-86ed-816455bf3bf8", "selectorKind": "participant", "selector": { "subject": { "id": "end-7", "kind": "participant" } }, "status": "pending", "phase": "seed", "requestedBy": "user:60328bcc-a6e4-4854-9442-fbdcc460838c", "matchable": true, "counts": {}, "attempts": 0, "createdAt": "2026-10-09T13:30:39.645Z"}Follow it, where <erasure-id> is its id:
pnpm exec kindgi memory erasures get <erasure-id>pnpm exec kindgi memory erasures listget shows the erasure:
status:pending,running,waiting-on-run,completedorfailed.phase:seed,expand,settle,erase, thendone.counts: what each store cleared so far.lastError: the last error it will retry.
From your app, in TypeScript, it's memory.erasures.create, get, list,
resume, export and replay. In Python, it's memory.create_erasure,
get_erasure, list_erasures, resume_erasure, export_erasures and
replay_erasures. Over HTTP, it's /v1/memory/erasures.
It takes effect at once
Section titled “It takes effect at once”From the moment an erasure starts:
- no memory read returns the facts it names, even before they're cleared;
- a new turn for the person, in their conversation or with their
participantId, is refused with409 erasure-in-progress. The CLI printsError [erasure-in-progress]: An erasure of this person's words is in progress: no new turn starts for them until it completes.; - writing a fact for them is refused the same way, and so is an agent's
kindgi_remember.
A turn of theirs that's already running or waiting isn't refused. In the
settle phase, before it clears anything, the erasure:
- cancels their waiting turns, and the run's history says
erased; - so withdraws the approvals those turns waited on, with
withdrawnBecause: run-cancelled; - waits for a turn that's executing.
Here it's waiting for a turn that's still executing:
{ "id": "044cd0b9-244e-471d-916c-431edc0dbc42", "selectorKind": "participant", "selector": { "subject": { "id": "end-61", "kind": "participant" } }, "status": "running", "phase": "settle", "requestedBy": "user:973bc704-cfcc-49be-8b3d-cdbe61dd51e8", "matchable": true, "counts": {}, "attempts": 0, "lastError": "not-yet:run-executing", "createdAt": "2026-10-09T12:45:29.410Z", "startedAt": "2026-10-09T12:45:29.417Z"}And the same erasure, completed. It keeps no trace of whom it erased: the
selector is gone.
{ "id": "044cd0b9-244e-471d-916c-431edc0dbc42", "selectorKind": "participant", "status": "completed", "phase": "done", "requestedBy": "user:973bc704-cfcc-49be-8b3d-cdbe61dd51e8", "matchable": true, "counts": { "kernel_runs": 3, "memory_facts": 1, "hitl_approvals": 1, "kernel_waitpoints": 1, "memory_embeddings": 0, "memory_log_entries": 4, "agent_conversations": 2, "agent_run_snapshots": 3, "conversation_recall": 4, "kernel_runs_cancelled": 1, "memory_fact_revisions": 1, "kernel_journal_entries": 95 }, "attempts": 0, "createdAt": "2026-10-09T12:45:29.410Z", "startedAt": "2026-10-09T12:45:29.417Z", "completedAt": "2026-10-09T12:45:41.704Z"}The turn it cancelled ends its journal like this, with nothing left of
what it held, where <run-id> is the turn's run id:
pnpm exec kindgi runs journal <run-id>{ "data": [ … { "sequence": 4, "kind": "wait.suspended", "nodeId": "setup", "timestamp": "2026-10-09T12:45:28.617Z" }, { "sequence": 5, "kind": "run.cancelled", "payload": { "reason": "erased" }, "timestamp": "2026-10-09T12:45:41.565Z" } ], …}Once it completes, new turns for the person start again.
What it clears
Section titled “What it clears”- Their facts, every revision, and their vectors. A fact written from an
erased one goes to review: a
memory-factapproval, reasonderived-from-erased. - Their conversations, with their messages and what recall indexed. The
conversation then answers
404. - The runs that served them: each run's input, output and journal, its
snapshots and waitpoints, and what it left in provenance. The run itself
stays, with
contentErasedAt. Correcting such a run is refused,410 run-erased. - The reasons in its ratings: a correction of such a run keeps its
verdict, who corrected it and when, and loses its reason,
what it says the run should have done
and the corrected item's value. An item still waiting for a correction
closes as
erased, and a dismissal's reason is cleared. - What else held their words:
- approvals' text, such as a tool call's arguments and a reviewer's rationale. The title, the status and who decided stay;
- audit events' text, and monitoring observations;
- a webhook delivery's error message, and a model call's error message (its cost stays);
- evals' copies. A test case copied from an erased run reads
erased: true, and runs on the test cases leave it out and count it, insummary.erased. For a case from such a run, or a replay of one, a run on the test cases loses what could quote them: the case'schanges,toolsanderror, and what differed from what the run should have done. Its scores, its counts, and whether each correction passed stay, so its numbers do.
Audit events of a kind your compliance setup holds, a legal hold, keep their
text, and the erasure counts them. A fact under legal hold stops the erasure
before it starts: 409 legal-hold, with the held facts in details.factIds.
What it can't reach
Section titled “What it can't reach”- What the runtime logged to your log system: keep production at
infoorwarn, as What logs hold explains. - Signed exports already made, such as an approval's audit bundle.
- A webhook receiver's copy of a delivery that was already sent.
- An Improve run's drafted instructions and hypotheses. A draft that quotes eight or more words in a row of a test case is refused, and such quotes are cut from a hypothesis, so they hold a paraphrase at most. The reviewer who approves a suggested change is the check.
Shared flows
Section titled “Shared flows”A person's turn can sit inside a flow that also serves other people.
Cancelling it now could end their work too. So the erasure waits for that
run, with the status waiting-on-run and waitingOn naming it. It waits
until a deadline, KINDGI_ERASURE_SHARED_WAIT_MS, 7 days by default. Then it
cancels the run and goes on. Erasing a person also clears the records of
flows they shared with others.
To act before the deadline, where <erasure-id> is the erasure's id:
pnpm exec kindgi memory erasures resume <erasure-id> # try again nowpnpm exec kindgi memory erasures resume <erasure-id> --force # stop waiting: cancel that run and go onEach prints the erasure as JSON, with its status and phase.
After a backup restore
Section titled “After a backup restore”Restoring a backup brings back what an erasure cleared, and rolls the erasure ledger back too. So export the ledger off-box regularly, and replay it after a restore:
pnpm exec kindgi memory erasures export --out=erasure-ledger.json--out is the file it writes. It prints:
{ "wrote": "erasure-ledger.json", "erasures": 1}After a restore, replay that file:
pnpm exec kindgi memory erasures replay erasure-ledger.jsonThe ledger keeps no identifier, only a keyed hash of whom each erasure
erased. A replay needs the runtime's erasure ledger key to find them again.
Without the key, an erasure still runs, but it answers with an
erasure-unmatchable warning. A replay can't match that erasure.
Erasures and backups
sets up the key and the restore.