Skip to content

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.