Skip to main content

GDPR Erasure

When a member exercises their right to erasure (GDPR Article 17 / AEPD), you must delete or anonymise their personal data. But you typically can't fully delete the row — loyalty laws and accounting rules require you to keep transaction-level evidence (raffle audit trails, balance reconciliation) for up to three years. The platform reconciles both: it scrubs personally identifiable information while leaving the financial trail attached to a PII-free, soft-deleted member row.

This is a one-way operation. There is no restore.

When to use it

  • A member explicitly asks to be forgotten.
  • A regulator instructs you to erase a profile.
  • Your data-retention policy says X years post-inactivity and you're processing a batch.

What gets scrubbed

The erasure walks several related tables:

TableAction
MemberName → DELETED. Email → deleted-{hash}@anonymized.local. Phone, external ID, date of birth, province, professional license, IBAN, IBAN holder, Bizum phone → cleared. Marketing opt-in → false. Status → Blocked. IsDeleted → true (soft-delete; the row stays for FK integrity).
MemberCardsQR/barcode payload cleared. IsActive → false.
EvidenceSubmissionsReceipt image deleted from blob storage. ImageUrl, ImageHash, OCR JSON → cleared.
MemberPushSubscriptionsHard-deleted (push endpoints are PII tied to a device).

The pseudonym used in the new email is SHA-256(email | phone | id) truncated to 16 hex characters — stable enough to recognise the row in logs, opaque enough to be useless on its own.

What is NOT scrubbed

These tables retain the foreign key to the member row but contain no member PII once the scrub completes:

  • Loyalty transactions — every points movement.
  • Member balances — for reconciliation.
  • Reward redemptions — for stock accounting.
  • Member consents — proof that consent was given (with reference to the terms version).
  • Raffle win/award rows — required by raffle audit law.

Spanish loyalty/raffle regulations require these to be retained for three years.

Step-by-step

Erase a single member

  1. Go to Loyalty → Members and find the member.
  2. Open their profile.
  3. Click GDPR → Erase.
  4. The platform shows a confirmation dialog summarising what will be cleared.
  5. Confirm. The operation runs in a single transaction.
  6. The result page shows the erasure certificate (see below) with the counts of redacted entities.

The row is now PII-free and soft-deleted. Searching for the member by name or email returns no results.

Export before erasing (SAR / right to access)

Members also have a right to access their data (GDPR Article 15). Before erasing, you can produce a full Subject Access Request dump:

  1. Open the member's profile.
  2. Click GDPR → Export.
  3. The platform produces a JSON file with profile, balances, transactions, evidences, redemptions, cards, OU assignments, and segments.
  4. Send the file to the member through a secure channel.

You can do export → wait for confirmation → erase as one workflow.

Audit the erasure

Every erasure writes a GdprErasureCertificate row with:

  • Tenant ID and member ID (the soft-deleted row).
  • Member code at time of erasure.
  • Requested at and completed at timestamps.
  • Operator — either admin:{userId} for staff-initiated erasures or self-service for member-initiated ones.
  • Entities redacted — a JSON dict with the count of rows touched in each table.

The certificate is your evidence that the erasure was performed. Keep it.

Limits and gotchas

  • One-way. No restore path. If a member changes their mind, they need to register again as a new account.
  • Soft-delete keeps the row but the row contains no PII. The FK from a loyalty transaction still resolves to a real (anonymous) member.
  • Push subscriptions are hard-deleted. A re-registered member will need to re-subscribe to push.
  • Don't erase a member with pending disbursements — pay them out or cancel first, otherwise you're holding a payable to a phantom row.
  • Permission: Pages.Members.GdprErase (and Pages.Members.GdprExport for the SAR dump).