CortexDB Docs
API Reference

/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}/manifest404. refcount_breakdown adds in_scope_events.

Execute

POST /v1/erasures202:

{ "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 409 guard does not enforce. Capturing new events in scope after a preview, then executing with that preview_id, returns 202 rather than requiring a fresh preview.
  • POST /v1/erasures/{id}/cancel405. Cancel could not be exercised (jobs complete near-instantly for small scopes).

The response header is x-cortex-stability: stable.

On this page