erasure operations
The exact fields each erasure.* operation takes and returns.
Every op is POST /v1/<op> over HTTP and the MCP tool loom_<op>. The identity fields (org, namespace, agent, user_id, session_id) are filled from the token and the MCP session when a transport can; for an HTTP call with no token on a single-user server, the install's org on every op but code.* and tracker.* (which keep their plane's), and on some ops its namespace and agent; see identities and namespaces. The operations index explains role, kind and the MCP all listing. Unknown input fields are served and named in ignored_args.
erasure.export
Export the rows about a user or namespace, each section capped at section_limit.
POST /v1/erasure.export · MCP loom_erasure_export · role admin · idempotent · audited
| Input field | Type | Default | Meaning |
|---|---|---|---|
org |
str |
required | The tenant (first element of the identity tuple). |
scope_type |
Literal['user_id', 'namespace', 'namespace_list'] |
required | user_id exports one user's rows across the org (org admin only); namespace exports one namespace (admin on it). |
scope_value |
str |
required | The user id or namespace to export. |
requester |
str |
required | email or service account requesting the export |
include_tombstoned |
bool |
True |
Include tombstoned memory and conversation rows. |
section_limit |
int |
10000 |
Per-section caps. |
| Output field | Type | Meaning |
|---|---|---|
exported_at |
datetime |
When the export was read (UTC). |
org |
str |
The tenant (first element of the identity tuple). |
scope_type |
Literal['user_id', 'namespace', 'namespace_list'] |
Echo of the requested scope kind. |
scope_value |
str |
Echo of the requested user id or namespace. |
requester |
str |
Echo of who asked for the export. |
counts |
dict[str, int] |
Rows returned per section (memory, knowledge, conversations, conversation_threads, agent_events, audit_log): at most section_limit, and the section's truncated says more exist. |
memory |
ErasureExportSection |
loom.memory rows in scope (under the Postgres authority, its heads); tombstoned rows too when include_tombstoned. |
knowledge |
ErasureExportSection |
Knowledge chunk rows of the namespace, or every one the user wrote for a user_id scope. |
conversations |
ErasureExportSection |
Turns and saved messages in scope with their sensitivity, up to erasure_export_conversation_max_bytes of text plus the turn that crosses it. |
conversation_threads |
ErasureExportSection |
Threads in scope with their title, metadata and sensitivity, capped by section_limit rows only (no byte budget). |
agent_events |
ErasureExportSection |
Per-request ledger rows in scope, newest first. |
audit_log |
ErasureExportSection |
Audit rows in scope; for user_id, rows whose subject mentions the user id. |
erasure.list
Audit-shaped roll-up of the org's erasures the caller may see: the org-wide view for an org admin, the namespace sagas of the caller's own namespaces otherwise.
POST /v1/erasure.list · MCP loom_erasure_list · role viewer · read · not listed under the MCP all toolset
| Input field | Type | Default | Meaning |
|---|---|---|---|
org |
str |
required | The tenant (first element of the identity tuple). |
limit |
int |
50 |
At most this many sagas, newest first; the server caps it at 500. |
status |
Literal['in_progress', 'completed', 'failed'] \| None |
None |
filter to one status if needed |
| Output field | Type | Meaning |
|---|---|---|
rows |
list[ErasureListRow] |
Recent sagas, newest first: org-wide for an org admin, otherwise only namespace sagas on namespaces the caller belongs to. |
erasure.request
Erase the rows about a user or namespace (the audit trail is kept).
POST /v1/erasure.request · MCP loom_erasure_request · role writer · idempotent · audited
| Input field | Type | Default | Meaning |
|---|---|---|---|
org |
str |
required | The tenant (first element of the identity tuple). |
scope_type |
Literal['user_id', 'namespace', 'namespace_list'] |
required | What scope_value names: user_id (one user across the org's namespaces, org admin only), namespace (one namespace, admin on it) or namespace_list (several, org admin only). |
scope_value |
str |
required | For namespace_list: a JSON array of namespace names (1–500), or a JSON object {"namespaces": [...], "purge": bool}. |
requester |
str |
required | email or service account that requested the erasure |
urgency |
Literal['normal', 'urgent'] |
'normal' |
normal or urgent; recorded on the saga's ledger row, the saga runs the same either way. |
purge |
bool |
False |
Hard-delete the rows of the soft-delete tables (memory, embeddings, chunks, documents, summaries, links) instead of stamping tombstoned_at: a tombstone keeps the bytes in every FINAL read and merge; a purge takes them out of the parts. |
external |
bool |
True |
External backends (Langfuse traces by tag, ClickStack OTel rows, …) run for every namespace of the scope and fail the saga closed. |
| Output field | Type | Meaning |
|---|---|---|
erasure_id |
str |
Id of the saga; pass it to erasure.status. |
status |
Literal['in_progress', 'completed', 'failed'] |
completed, or failed when a step or the residue check failed; the recovery worker retries a failed saga. |
steps |
list[_ErasureStepResult] |
One entry per saga step with the rows it touched; a step done by an earlier attempt reads rows_affected 0 and a skipped note. |
requested_at |
datetime |
When the saga was registered (UTC). |
completed_at |
datetime \| None |
When the saga reached its terminal state (UTC); None while it runs. |
residue_count |
int |
Verified forgetting: live rows and bucket objects the post-erasure census still found in scope, per substrate; 0 / {} on a completed saga. |
residue |
dict[str, int] |
Rows still live per table after the saga, only tables with a non-zero count (the Postgres authority's under pg:<table>), and the bucket objects left, under the key objects.document_originals; {} on success. |
verified_at |
datetime \| None |
When the residue census ran (UTC); None if it never ran or left a store uncounted (the Postgres authority or the bucket, which the server log names); residue then holds what it did count. |
census |
dict[str, int] \| None |
The receipt: every count of this run's census, zeros included; None when no census counted every store (none ran, or one left a store uncounted). |
purge |
bool |
Whether the saga deletes rows (true) or tombstones them; a single namespace's never does. |
failure_reason |
str |
Why a failed saga failed, "" otherwise: <step>: <error> or a bare <error> outside a step, where <error> is the exception's kind or a Loom error's <CODE>: <message>, or `verify.residue: ErasureResidueError: residue after erasure… |
steps_completed |
list[str] |
Names of the steps finished so far, sorted. |
erasure.status
Check the status of an in-flight erasure request (queued, running, completed, failed).
POST /v1/erasure.status · MCP loom_erasure_status · role viewer · read · not listed under the MCP all toolset
| Input field | Type | Default | Meaning |
|---|---|---|---|
erasure_id |
str |
required | Id returned by erasure.request. |
| Output field | Type | Meaning |
|---|---|---|
found |
bool |
False for an unknown id and for a saga the caller may not read (the two look alike). |
erasure_id |
str |
The saga id asked about. |
org |
str \| None |
The tenant (first element of the identity tuple). |
scope_type |
str \| None |
The saga's scope kind (user_id, namespace or namespace_list); None when not found. |
scope_value |
str \| None |
The saga's scope as requested (user id, namespace, or the JSON list). |
requester |
str \| None |
Who asked for the erasure, as given in the request. |
status |
Literal['in_progress', 'completed', 'failed'] \| None |
in_progress, completed or failed; None when not found. |
current_step |
int \| None |
How many steps have finished; 99 once the saga completed. |
requested_at |
datetime \| None |
When the saga was registered (UTC). |
completed_at |
datetime \| None |
When the saga reached its terminal state (UTC); None while in progress. |
failure_reason |
str \| None |
Why a failed saga failed, None otherwise: a reason erasure.request names, or max_attempts (N) exceeded once recovery gave up, then ; last: <reason> if any; the saga failed; the error is in the server log for a reason in neither… |
residue_count |
int |
Verified forgetting. |
residue |
dict[str, int] |
Rows still live per table at the last census (the Postgres authority's under pg:<table>), and the bucket objects left, under the key objects.document_originals; {} when none. |
verified_at |
datetime \| None |
When the last residue census ran (UTC); None if it has not run or left a store uncounted (the Postgres authority or the bucket, which the server log names); residue then holds what it did count. |
census |
dict[str, int] \| None |
The receipt: every count of the latest run's census, zeros included; None when the latest row holds no census that counted every store (a run in progress, none ran, one left a store uncounted, or a build that keeps no census wrote the… |
purge |
bool |
Whether the saga deletes rows (true) or tombstones them; a single namespace's never does. |