> ## Documentation Index
> Fetch the complete documentation index at: https://internal.softcrum.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Erasure and Portability (ARCO Rights)

> After this ships, a person can ask to be deleted and it actually happens across the whole suite, within a committed deadline, with proof it happened.

## Context

Ley 21.719 is fully in force on 2026-12-01, and erasure is the right that most directly conflicts
with how the rest of the system is built. Points ledgers are append-only for accounting integrity;
audit logs exist precisely so nothing disappears.

DEC-J3 resolves the conflict without pretending it does not exist: **the profile and events are
deleted; ledger rows are anonymized to a tombstone**. Anonymization satisfies the right — the data
is no longer attributable to a person — while the accounting stays whole. The committed deadline is
≤30 days and the process is automated, because a manual process is one that is late during the
month everyone is busy.

Orchestration lives in core rather than in each module. One place decides what erasure means, and
the modules obey.

## Scope *(normative)*

* `core.subject_requests`: erasure and portability requests with their lifecycle.
* Erasure orchestration across every module, with per-module acknowledgement.
* Contact and event deletion; ledger anonymization instructed to loyalty.
* Portability export: everything about the subject in a structured, common format.
* Proof of execution retained after the data is gone.
* The 30-day SLA with escalation before it is breached.

## Non-scope *(normative)*

* Verifying the requester's identity. That is the tenant's obligation as controller (DEC-J1); we
  execute an instruction from an authenticated tenant.
* The member-facing request form — `frontend/portal`.
* Deleting a tenant. That is offboarding, a different runbook.

## Behaviour *(normative)*

1. A request has a lifecycle: `received → validated → executing → completed`, with a timestamp at
   every step. The timeline is the evidence.
2. The SLA is **≤30 days** and escalates at day 21. Discovering a breach on day 31 is discovering
   it too late.
3. Erasure is orchestrated: core instructs each module and **waits for acknowledgement**. A module
   that fails to acknowledge blocks completion — a partial erasure reported as complete is the worst
   possible outcome.
4. Per module: `core` deletes the profile, identities, consents and events. `loyalty`
   **anonymizes** ledger rows to a tombstone and deletes nothing. `messaging` deletes send content
   and keeps aggregate counts. `crm` deletes notes and activities.
5. Suppressions **survive erasure**, as a hashed identifier with no profile attached. Deleting the
   suppression would let the same address be emailed again, which is the opposite of what the person
   asked for.
6. **Proof of execution survives**: an audit row records that the erasure happened, for whom (as an
   opaque reference), when, and which modules acknowledged. The proof is the one thing that must
   outlive the data.
7. Portability exports everything about the subject in a structured, common format, delivered
   through a signed link that expires.
8. An erasure is **irreversible and confirmed before execution**. There is no undo, and the API says
   so.
9. Backups age out on their own retention; the request records that and does not claim otherwise.
   Claiming instant deletion from backups would be a claim we cannot support.

## Data *(normative)*

| Table                   | Key invariants                                                                                                                                                                                                                 |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `core.subject_requests` | `tenant_id`; `contact_ref` opaque after execution; `kind` erasure\|portability; `status`; `received_at`, `due_at`, `completed_at`; `module_acks` JSONB; append-only except status transitions; retained beyond the data itself |

## API *(normative)*

| Endpoint                              | Class      | Permission                   | Budget             |
| ------------------------------------- | ---------- | ---------------------------- | ------------------ |
| `POST /v1/core/contacts/{id}/erasure` | Management | `core.contacts.erase`        | p95 \<1 s (async)  |
| `GET /v1/core/contacts/{id}/export`   | Management | `core.contacts.export`       | async, signed link |
| `GET /v1/core/subject-requests`       | Management | `core.subject_requests.read` | p95 \<1 s          |

## Events *(normative)*

`core.contact.erased` on the outbox, so every consumer — including a tenant's own webhook — can
drop its copy. A tenant that mirrored our contacts must be told.

## Acceptance criteria *(normative)*

1. Erasure deletes the profile, identities, consents and events, and leaves ledger rows anonymized
   with the program's total outstanding points **unchanged**.
2. A module that does not acknowledge blocks completion, and the request stays `executing`.
3. The proof-of-execution audit row survives and names the modules that acknowledged.
4. A suppression survives as a hashed identifier and still blocks a send to that address.
5. Portability export contains every category listed in the RAT and is readable as valid JSON.
6. The export link expires and is rejected afterwards.
7. Day-21 escalation fires for a request not yet complete.
8. **Negative:** no erasure path deletes a ledger row, and no completion is reported without every
   module's acknowledgement.

## Execution

Asynchronous pipeline. Orchestrator in `backend/workers`, SLA monitoring in `backend/scheduler`.
Each module exposes an internal erasure handler; core never reaches into another module's tables.

## Open questions

| # | Question                                                                                          | Decides          | By                       |
| - | ------------------------------------------------------------------------------------------------- | ---------------- | ------------------------ |
| 1 | Does the tenant have a window to object before execution, or is erasure immediate on request?     | Daniel + abogado | before commercial launch |
| 2 | How long do we retain `subject_requests` after completion — is there a maximum, or is it forever? | Daniel + abogado | before commercial launch |

## Changelog

| Version | Date       | Change        | Why | Author                 |
| ------- | ---------- | ------------- | --- | ---------------------- |
| 0.1.0   | 2026-08-17 | Initial draft | —   | daniel + claude-opus-5 |

## Delivery record

*Not implemented yet.*
