Automatically Purging Personal Data Once Its Retention Period Expires with n8n (GDPR)
Published 5 August 2026 · 6 min read
A company's GDPR records of processing activities almost always list a retention period next to each data category: "prospects — 3 years," "rejected applications — 2 years," "connection logs — 1 year." The problem isn't knowing these periods, it's enforcing them. A "retention period" field in a spreadsheet or a Supabase table stays an intention until someone actually deletes the rows it applies to, and nobody ever does that spontaneously — a database grows, it doesn't purge itself. Yet that's exactly the mechanism Article 5(1)(e) of the GDPR, the storage limitation principle, requires: keep identifiable data only as long as strictly necessary for its purpose. A team of researchers — Garg, Goldwasser, and Vasudevan, in a paper presented at Eurocrypt 2020 — formalized just how much more demanding a genuinely effective deletion is than a simple DELETE: it must also eliminate the data's derived effects (copies, indexes, structures built from it), or the trace lingers elsewhere in the system (study on Google Scholar). This guide builds an n8n workflow that turns a retention period from a line in a register into a purge that actually runs.
What automatic purging covers — and what it doesn't
Three related GDPR automations respond to different triggers, and they shouldn't be confused:
- The records of processing activities document what data exists and for how long — see our guide on GDPR records of processing activities with n8n.
- Handling GDPR requests responds to someone exercising their right to erasure at a specific moment — see our guide on handling GDPR requests with n8n.
- Automatic purging, the subject of this article, doesn't depend on any request: it runs systematically, on any data that has passed its declared retention period, whether anyone asks for it or not.
This purge also has nothing to do with removing duplicates: a duplicate is a data-entry error, whereas expired data is legitimate data that has simply passed its legal shelf life.
Why manual purging never holds up over time
Manually sorting expired data means cross-referencing three things every single time: the data category, its creation or last-activity date, and the applicable retention rule. Done by hand, this cross-referencing takes time, nobody prioritizes it over more visible tasks, and it eventually stops happening at all — until an inspection day when the company discovers it's still holding CVs from candidates rejected six years ago. The only way to sustain a retention period over time is to take it out of human memory and encode it in a scheduled trigger.
Step 1 — A retention policy table
Rather than hard-coding retention periods into the workflow, a Supabase table centralizes the rules, editable without touching any code:
create table retention_policies (
id uuid primary key default gen_random_uuid(),
category text unique not null,
target_table text not null,
date_column text not null,
duration_months integer not null,
action text not null default 'delete',
legal_basis text,
active boolean default true
);
insert into retention_policies
(category, target_table, date_column, duration_months, action, legal_basis)
values
('inactive_prospects', 'crm_contacts', 'last_interaction', 36, 'delete', 'legitimate interest — prospecting'),
('rejected_applications', 'candidates', 'rejection_date', 24, 'delete', 'consent — recruitment'),
('closed_support_tickets', 'tickets', 'closed_date', 60, 'anonymize', 'legal obligation — warranty'),
('login_logs', 'connection_audit', 'created_at', 12, 'delete', 'legitimate interest — security');
These durations (3 years for prospecting, 2 years for a rejected application…) reflect common benchmarks recommended by supervisory authorities like France's CNIL, but every company needs to validate its own against its own legal bases — this isn't universal legal advice, it's the structure that holds your decision.
Step 2 — Detecting expired rows
A workflow scheduled once a week (a Schedule Trigger) reads retention_policies, then for each active policy dynamically builds a query against the target_table:
select id from crm_contacts
where last_interaction < now() - interval '36 months';
Since the table and column names come from configuration rather than code, this same generic sub-workflow applies to every policy without duplication — the same parameterization principle described in our guide on n8n sub-workflows. Each matching row is added to a purge_candidates queue rather than deleted immediately.
Step 3 — Deletion or anonymization, depending on the case
The action declared in the policy determines what happens next:
- Deletion: a targeted
DELETEon the record's ID, inside a transaction that fails cleanly if a foreign-key constraint blocks the operation rather than forcing it through with an uncontrolled cascade. - Anonymization: identifying columns (name, email, phone number) are replaced with generic values or an irreversible hash, while non-identifying columns useful for statistics are kept — the same masking logic detailed in our guide on anonymizing data before an LLM call, applied here to a database rather than a live flow.
Step 4 — What a database deletion doesn't cover
A DELETE on the main table is necessary but rarely sufficient. Personal data leaves copies elsewhere: automatic Postgres backups (see our guide on PostgreSQL backup and restore), fragments indexed in a vector database if the contact was ingested for a RAG chatbot, or simply execution payloads that n8n itself retains. The purge can't chase down every encrypted backup, but it must at minimum document that scope: the backup retention policy itself needs to be aligned with the longest retention period it contains, and any embedding tied to a deleted contact needs to be removed from the index — a topic covered in our guide on updating a RAG index.
Step 5 — Never purge in bulk without human validation
A misconfigured retention policy (the wrong date column, a duration in days instead of months) can turn a routine purge into a major incident. The workflow should never run a mass deletion without a safeguard: past a threshold (say, 50 rows in a single run), it switches to a human-approval step before continuing, following the same pattern as our guide on human approval with Wait and Slack. Below the threshold, the purge runs directly — routine cleanup doesn't need a human in the loop on every single pass.
Step 6 — Logging the purge itself
Paradoxically, the action of deleting a trace needs to leave one of its own: which policy triggered the purge, how many rows were processed, on what date, under which workflow ID. This logging feeds the same append-only table described in our guide on building a GDPR audit trail with n8n and Supabase — it's the only way to answer a regulator who asks, "prove you deleted those CVs after two years," without being able to show the CVs themselves, since they no longer exist.
What this automation doesn't replace
This workflow enforces rules that were already validated by a human in the records of processing activities; it doesn't decide retention periods or legal bases on its own, and it doesn't remove the need to check that backups and third-party systems follow the same schedule. Its role is to close the gap between a written policy and a policy that's actually enforced — the gap where nearly all the failures found during inspections tend to live.
In summary
A retention period noted in a register protects no one until some mechanism actually enforces it on the real data. A centralized policy table, a scheduled detection of expired data, a deletion or anonymization action depending on the case, a human-approval threshold for bulk purges, and logging of the operation itself: these five pieces are enough to turn Article 5(1)(e) from a line of legal text into a process that runs on its own. The workflows in the Compliance & Audit Pack (€149) apply the same timestamped traceability discipline to your questionnaires and audits; combined with the RAG Assistant Pack (€119) for the vector-database side, or the Full FlowKit Bundle (€269) to cover everything at once, they give you a solid foundation so that data retention stays what it's supposed to be: a duration, not an eternity by default.
FAQ
Frequently asked questions
Does automatic purging replace the records of processing activities?
No, it depends on it. The register documents the intended retention period for each data category; the purge is the mechanism that actually enforces that period on the real data. Without an up-to-date register, a purge workflow has no reliable source telling it what to delete and when.
Should expired data be deleted or anonymized?
It depends on the remaining use case. If the data has no further purpose, permanent deletion is the right call. If it still holds statistical value (sales volumes, support ticket trends) without needing to identify the person, irreversible anonymization is enough and lets you keep the data usable for longer — the same logic covered in our guide on anonymizing data before an LLM call.
Isn't a simple scheduled SQL DELETE enough?
Technically it does the job on the main table, but it almost always leaves traces elsewhere: backups, vector indexes, n8n execution logs, application caches. A structured purge workflow doesn't just empty a table — it documents what was deleted, where, and when, so you can answer an inspection that asks for proof of erasure.
Bundle FlowKit Complet
€269