AI Agents and GDPR: PII Redaction in Production
Your AI agent just processed a contract containing a customer's full name, national ID number, and bank account. It then sent all of that text — unencrypted, as a JSON payload — to servers in the United States.
Do you have documented legal basis for that international data transfer? Have you completed the Data Protection Impact Assessment that GDPR requires for high-risk processing?
Most engineering teams building LLM-powered workflows don't ask these questions until someone from legal does. And when that moment arrives, the most common answer is to halt the system or rebuild it from scratch.
The problem nobody sees until it explodes
When you build an AI agent that processes documents — contracts, invoices, customer emails, support histories — that agent sends the content to the LLM provider's infrastructure for processing. This means your customers' personal data travels to third-party infrastructure.
GDPR requires a valid legal basis for that processing and, when data leaves the EU, adequate safeguards: standard contractual clauses, or transfer to a country with an adequate protection level.
Anthropic, OpenAI, and most LLM providers offer Data Processing Agreements (DPAs) and commit to not using API data for model training. That covers part of the problem — but not all of it:
- Your liability doesn't disappear after signing a DPA — you remain the data controller
- The data minimization principle requires sending only what is strictly necessary for the specific purpose
- Regulated sectors (banking, healthcare, insurance) have stricter requirements that a generic DPA doesn't cover
The technical question is: can your agent do its job without the LLM ever seeing the raw personal data?
In most cases, yes.
The pattern: PII Redaction as a security layer for GDPR-compliant AI agents
The solution is an anonymization pipeline that sits between your system and the LLM:
- Detection: scan the input text to identify PII entities (names, IDs, IBANs, emails, phone numbers, addresses)
- Substitution: replace each entity with a pseudonymous token:
"Jane Smith"→<PERSON_1>,"GB82WEST12345698765432"→<IBAN_1> - Processing: send the anonymized text to the LLM, which does its analysis without ever seeing real data
- Re-insertion: the LLM output contains the same tokens, which are substituted back with the original values
The LLM never sees real personal data. Your system holds the token ↔ value mapping in memory during the session and applies it at the end.
TypeScript implementation with Microsoft Presidio
The most widely used open-source library for this in production is Microsoft Presidio, available as a REST service in Python. From your Next.js or Node.js application, you call it like any other microservice:
type PiiEntity = {
start: number;
end: number;
entity_type: string;
score: number;
};
type RedactionMap = Map<string, string>;
async function redactPii(text: string): Promise<{
redacted: string;
map: RedactionMap;
}> {
const entities: PiiEntity[] = await fetch(
"http://presidio-analyzer:5002/analyze",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ text, language: "en" }),
}
).then((r) => r.json());
const sorted = entities
.filter((e) => e.score > 0.7)
.sort((a, b) => b.start - a.start);
const map: RedactionMap = new Map();
const counters: Record<string, number> = {};
let redacted = text;
for (const entity of sorted) {
const original = text.slice(entity.start, entity.end);
counters[entity.entity_type] = (counters[entity.entity_type] ?? 0) + 1;
const token = `<${entity.entity_type}_${counters[entity.entity_type]}>`;
map.set(token, original);
redacted =
redacted.slice(0, entity.start) + token + redacted.slice(entity.end);
}
return { redacted, map };
}
function reinsertPii(text: string, map: RedactionMap): string {
let result = text;
for (const [token, original] of map) {
result = result.replaceAll(token, original);
}
return result;
}
async function processContractSafely(contractText: string) {
const { redacted, map } = await redactPii(contractText);
const response = await anthropic.messages.create({
model: "claude-opus-5-5",
max_tokens: 1024,
messages: [
{
role: "user",
content: `Extract the key clauses from this contract:\n\n${redacted}`,
},
],
});
const rawOutput =
response.content[0].type === "text" ? response.content[0].text : "";
return reinsertPii(rawOutput, map);
}
For teams that don't want to manage an external microservice, regex patterns cover the most common PII types:
const PII_PATTERNS: Record<string, RegExp> = {
SSN: /\b\d{3}-\d{2}-\d{4}\b/g,
IBAN: /\b[A-Z]{2}\d{2}[\s]?\d{4}[\s]?\d{4}[\s]?\d{4}[\s]?\d{4}[\s]?\d{0,4}\b/g,
PHONE: /\b(?:\+1[\s-]?)?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}\b/g,
EMAIL: /\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\b/g,
};
The limitation of regex is that it won't catch proper names or physical addresses. For that, Presidio's NER (Named Entity Recognition) model delivers significantly more robust results.
What GDPR actually requires for AI agents
It's important not to over-engineer this. GDPR doesn't prohibit using LLMs to process personal data — it requires legal basis and technical measures proportionate to the risk.
For most business use cases (contracts, emails, invoices), pseudonymization is sufficient as a technical safeguard, provided you also:
- Have a signed DPA with your LLM provider
- Document the DPIA for high-risk processing
- Limit data retention in LLM logs
- Maintain an up-to-date processing register
For regulated sectors (banking, healthcare, insurance) the requirements are stricter. In some cases it's necessary to use models deployed on your own infrastructure or within the EU. If you're building an AI automation agent for one of these sectors, compliance analysis is part of architecture design — not a last-sprint concern before launch.
The real cost of getting this right from day one
Teams that implement this pipeline before scaling their agents save weeks of reactive work when the first audit arrives or a customer exercises their right of access.
The cost of doing it right from the start is one sprint. The cost of fixing it when data is already in production is a project.
If you have an AI integration that processes documents with customer data and you want to bulletproof the architecture from the beginning, let's talk.