/v1/erasures
GDPR reference-counted erasure — preview, execute, and poll the job. The true WAL-deletion path.
Erasures are CortexDB's reference-counted GDPR deletion path — the sanctioned exception to the append-only WAL. Unlike forget (which redacts or drops derived layers), a completed erasure deletes events from the WAL. The flow is preview → execute → poll.
Preview
POST /v1/erasures/preview with { scope, audit_note } → 200:
{
"preview_id": "ervw_01HX...",
"scope": "org:acme/user:alice",
"estimated_affected": { "...": "counts" },
"refcount_breakdown": {
"in_scope_events": 12,
"events_to_delete": 9,
"events_to_redact": 3,
"events_under_legal_hold": 0
},
"estimated_duration_ms": 420
}Cloud-only preview blocks
The cross_scope_propagation, legal_holds, and manifest_url blocks the v1 docs showed are not
returned self-hosted (likely cloud-only) — and GET /v1/erasures/preview/{id}/manifest → 404.
refcount_breakdown adds in_scope_events.
Execute
POST /v1/erasures → 202:
{ "erasure_id": "er_01HX...", "status": "running", "manifest_url": "/v1/erasures/er_01HX...", "lifecycle_stream": "..." }Execute requires confirm_all or an explicit selector
With from_preview_id set but no selector.memory_ids, execute → 422 EMPTY_SELECTOR_WITHOUT_CONFIRMATION — an erasure with no selector erases the entire scope, so you
must pass confirm_all: true (or a selector.memory_ids list to erase specific records without a
preview). The manifest_url in the response is /v1/erasures/{id} (no /manifest suffix).
{
"scope": "org:acme/user:alice",
"from_preview_id": "ervw_01HX...",
"confirm_all": true,
"audit_note": "DSR #1234"
}Status
GET /v1/erasures/{id} → the job record:
{
"erasure_id": "er_01HX...",
"status": "completed",
"phase": "audit",
"summary": {
"deleted_events": 9,
"redacted_events": 3,
"deleted_facts": 0,
"deleted_episodes": 0,
"deleted_concepts": 0,
"artifacts": 0,
"artifacts_pending_reevaluation": 0
}
}Status shape (v0.9.9)
The progress field is summary (not progress), there is no fraction_complete and no
audit_id, and phase can be audit (a 5th phase beyond enumerate → refcount → delete → cleanup).
Backend failures surface as 502 ERASURE_BACKEND_FAILED (retriable — erasure is idempotent), and an
accepted job can still finish with status: "failed" and an error field — always poll rather than
assuming acceptance means completion.
Known gaps (v0.9.9 self-hosted)
Stale-preview 409 is not enforced; cancel is not wired
Two documented safety behaviors do not fire on v0.9.9 self-hosted:
- The stale-preview
409guard does not enforce. Capturing new events in scope after a preview, then executing with thatpreview_id, returns202rather than requiring a fresh preview. POST /v1/erasures/{id}/cancel→405. Cancel could not be exercised (jobs complete near-instantly for small scopes).
The response header is x-cortex-stability: stable.