EMZETT.
Login

Account Deletion with an Objection Period (Soft Delete + Anonymisation)

In short: Instead of irrevocably removing an account immediately when deletion is requested, only a point in time is recorded at first (pending_deletion_at) — the account is only actually anonymised after a period has expired.

In more detail: An immediate, hard deletion is risky: an accidental click, a compromised account, or withdrawing the deletion request could otherwise no longer be undone. The period leaves room for an “undo” without contradicting the right to erasure (Art. 17 GDPR) — after the period expires, personal data is removed/anonymised (anonymizedAt records WHEN this happened).

Our context: At Emzett, db/migrations/add_v27_session_version_deletion.sql adds users.pending_deletion_at and users.anonymized_at — a 7-day objection period before the actual anonymisation.

In Depth

Not legal advice, but a technical classification. The right to erasure (Art. 17 GDPR) doesn’t necessarily require an immediate, hard deletion — it requires personal data to be erased without undue delay once the purpose of storing it no longer applies. A short, clearly communicated objection period with a clear note on how the deletion request can be withdrawn is generally considered justifiable, as long as it isn’t misused to effectively drag out the deletion.

Technically, two stages are distinguished: soft delete (the record is kept completely, only a flag/timestamp marks it as “scheduled for deletion” — reversible) and anonymisation (personal fields are replaced by placeholders that can no longer be traced back — irreversible). Important for real anonymisation: it isn’t enough to delete only obvious fields such as name/email — indirect identifiers too (a unique combination of date of birth + postcode, a referenced, otherwise empty order history) can make a person re-identifiable if they are left in place.

-- Schedule (reversible)
UPDATE users SET pending_deletion_at = now() + interval '7 days' WHERE id = $1;
 
-- After the period expires (irreversible, via cron job)
UPDATE users SET email = 'deleted-' || id || '@anon.local', name = 'Deleted user', anonymized_at = now()
WHERE pending_deletion_at < now() AND anonymized_at IS NULL;

See also: GDPR data export (Art. 15/20), GDPR