Korely

Users & agents

Erase a user

Permanently erase every memory and fact of one end user in the key's project, in a single call. Korely keeps a receipt that holds counts and a pseudonym, never the content.

DELETE /v1/users/{end_user}/memories

SDK: korely.delete_all(user_id=end_user). Use it when one of your customers asks you to erase their data. The step-by-step guide, with a test user, is Forget a user. The Glossary defines the words used across the docs.

This call deletes for good. Try it on a test end user first.

This is a physical delete. The rows are removed from the live database, and the content cannot be read back through the API, whatever parameter you send. On the Cloud, encrypted nightly backups keep a copy for up to 35 days.

What Korely keeps carries no erased content and never the id itself, only a pseudonym of it: a code of erased: and 32 hex characters that Korely computes from the id with a secret key. That is enough to show that the erasure happened:

  • the erasure receipt, whose id is the audit_id of the response;
  • an erase event in the audit log;
  • the audit-log rows of calls that named this end user (kept for 400 days), earlier erasure receipts, and the records of webhooks already delivered, all relabelled with the same pseudonym.

GET /v1/audit?user_id=<the same id> computes the pseudonym and finds the trail rows. A Self-hosted install with no SECRET_KEY setting has no pseudonym key: the id is dropped and nothing replaces it, so the proof is the audit_id.

Authentication

HTTP header, required: Authorization: Bearer kor_live_.... The key must carry the memories:write permission (the API calls it a scope), and belong to the project that holds the end user.

Path parameter

ParameterTypeDescription
end_userstringThe end-user namespace to erase, the user_id exactly as written. Every memory and fact scoped to this value in the key's project is deleted. Match it character by character, case and spaces included. Percent-encode it as one path segment (acme/42 is acme%2F42); any stored id is accepted. An unknown end_user is not an error: it returns 200 with zero counts and a new audit_id, so a mistyped id looks like success. An empty one, one longer than 255 characters, one with a NUL byte, or one that is an erased pseudonym (erased: and exactly 32 lowercase hex characters), is refused with 422 invalid_request.

This endpoint takes no query parameters and no request body.

Example request

Terminal window
curl -X DELETE https://api.korely.ai/v1/users/customer-giulia-4812/memories \
-H "Authorization: Bearer kor_live_..."

Response

200 OK. The end-user namespace that was erased, the counts of what was deleted, and the id of the erasure receipt.

{
"user_id": "customer-giulia-4812",
"memories_deleted": 14,
"facts_deleted": 31,
"erasure": "permanent",
"audit_id": "aud_5f0c2d9e8b7a4c1d9e3f6a2b1c0d4e8f"
}
FieldTypeDescription
user_idstringThe end user that was erased (echoes the path parameter).
memories_deletedintegerCount of memories physically deleted, memories forgotten earlier included.
facts_deletedintegerCount of facts physically deleted, closed ones included.
erasurestringAlways permanent: nothing erased here can be read back through the API.
audit_idstringId of the erasure receipt Korely keeps (counts and a keyed pseudonym of your end-user id, never content): aud_ and 32 hex characters. It comes once, in this response, and no call reads it back: store it with your own erasure record.

Up to version 0.1.42 the response also carried memories_forgotten and facts_invalidated, the same two numbers under older names. From 0.2.0 they are gone: read memories_deleted and facts_deleted.

Errors

StatusCodeCause
401invalid_keyMissing or invalid kor_live_ key, Invalid or missing API key: ..., then what is wrong.
403forbiddenThe key lacks the memories:write scope, API key missing required scope(s): memories:write; or the key is a confined key that does not cover this end user.
422invalid_requestend_user is empty, longer than 255 characters, holds a NUL byte, or is an erased pseudonym (erased: and exactly 32 lowercase hex characters).
429rate_limit_exceededThe account hit its rate limit. The response carries a Retry-After header.
503unavailableThe database is busy with another change to the same data. Retry after the seconds in Retry-After.

On every error above, nothing was erased. Branch on code, not on message.

Notes

  • Stop writing for that id first. A write after the call creates the end user again.
  • One transaction. Memories, facts, the names declared for the end user and the vocabulary Korely learned from its facts are erased in one database transaction. Items of POST /v1/batch imports that name the end user are scrubbed from the import, queued or finished.
  • Not erased. Memories written without a user_id, or under another id, stay even if they name the person. Copies outside Korely are yours: your logs and databases, and the model provider that read the text to extract facts.
  • Every agent_id. The call covers the end user's data under all agent namespaces of the project.
  • Distinct from deleting one memory. DELETE /v1/memories/{id} forgets one memory (it drops out of every read and its row stays) and, with erase=true, erases that one memory for good (REST only; the SDKs' delete() does not send it).
  • Scoped to the key's project. If the same end user lives in several projects, call it once per project, with that project's key. The same id in another project stays intact.
  • Names declared in other projects. The names declared with Declare a name are erased in the key's project. A name from before version 0.2.0 that Korely could not place in a project goes with the last erasure of the person, when nothing of them is left in another project. Names declared in another project of the account stay.
  • No 404, and a timeout is safe to repeat. If the first call succeeded, the second returns zeros and a new audit_id; the first audit_id cannot be read back.
  • Two records of the call. Each call writes the receipt (audit_id) and an erase event in GET /v1/audit, with the counts in meta. The event does not carry the audit_id and does not show which key made the call. Its user_id is the pseudonym (erased:...); ask GET /v1/audit?user_id=<the id> and it is found. Deleting one memory or forgetting one fact also writes an erase event: yours is the one whose meta.endpoint is users:delete.
  • Keep your own receipt. Store the audit_id and the time of the call.
  • Do not build the list to erase from List users. Since 1.0.23 it shows every end user Korely holds anything of, those with nothing live included, but a batch not imported yet is not read for it, and it comes one page at a time. Up to 1.0.22 it showed only end users with active data. Build the list from the ids you wrote.

Related