See llms.txt for all machine-readable content.
This sub-workflow redacts common personal data from any incoming item using a policy stored in n8n Data Tables, returns a redacted payload plus a detection report to the calling workflow, and writes an audit entry to a redaction_log table (with an optional Discord self-test notification).
payload and requested_by).redaction_policy n8n Data Table and attaches them to each incoming item.redaction_log n8n Data Table with counts, mode, requester, and payload size (but never the original values).redaction_policy and redaction_log n8n Data Tables and seed the default policy.pseudonym_key value in the redaction_policy table (the workflow refuses to pseudonymise while it is empty).{ payload, requested_by } or send the record directly as the item JSON.This is a tool other workflows call, not a workflow that runs on its own, and it is deliberately model-free. A redactor that sent personal data to a model in order to decide what counts as personal data would be adding a confident opinion about text it cannot verify, at the exact moment you least want one. Everything here either matches a validated pattern or it does not.
The part worth reading twice is what it does when it cannot do its job. An empty policy table refuses, and says so: an empty policy is not evidence that there is nothing to redact. Pseudonymise with no key refuses, and says why. In both cases the item comes back untouched with values_redacted at zero, the refusal is written to the audit log with its reason, and nothing is half-redacted. That was tested by deleting all eight policy rows and by running pseudonymise with the shipped empty key.
Validation is the difference between this and a regular expression. 4111 1111 1111 1111 is redacted as a card. 4111111111111112 is one digit off, fails Luhn, and is left in place, because it is far more likely to be an order number. DE89370400440532013000 is redacted as an IBAN and DE89370400440532013001 fails mod-97 and survives. A 21 digit reference number is past the E.164 limit of 15 and is not a phone number. Every one of those is a look-alike probe in the shipped self test, and they matter more than the positive cases: a detector that fires on everything turns every invoice number into a redacted card.
What it cannot do is more important than what it can. Personal names are not detected, because no pattern distinguishes a person from a place or a product, and pretending otherwise would be the worst kind of false confidence in a tool like this. Instead, anything that looked like it might identify somebody is returned as an unclassified candidate, and every answer carries the sentence that a result with nothing redacted is not evidence that the item holds no personal data. Names in a non-Latin script are missed entirely.
The audit log holds counts and detector names, never the values. A redactor that wrote the originals into a log table would have redacted nothing, just moved the problem, so redaction_log records when, who asked, which mode, payload_chars for the size of the item once serialised, how many values were replaced, how many were unclassified, the per-detector counts and the reason. That is enough to answer how much personal data is flowing through a pipeline without storing any of it.
Run Set Up And Self Test once and it does three things: creates the two tables with typed columns, loads the default policy, and pushes eight known probes through the same detector every caller uses, then posts the result. Five of the probes must be redacted and three must not. If someone later loosens a pattern, the self test says so rather than the change being noticed in production.
Known limits, stated plainly. Personal names, postal addresses and dates of birth are not detected. Non-Latin scripts are missed. Pseudonym tokens are stable only while the key is unchanged, and there is no rotation path. Nothing has been tested above a few hundred characters per item, and two callers writing the audit log at the same instant was never tried. The detectors are ordered so the validated ones consume their digits before the loosest one runs, which is a real dependency: adding a detector at the wrong position in that list could change what the others see.