HR & Recruiting AI Agents That Stay GDPR-Compliant
AI in recruiting and HR has specific failure modes — bias, automated decisions on candidates, retention of personal data. Here's how to ship an HR agent that doesn't trip Article 22, 5, or 6.
AI in HR is high-risk in the GDPR sense — automated decisions about people. Article 22 grants candidates the right not to be subject to a decision based solely on automated processing where the decision produces legal or similarly significant effects. A hiring rejection produces a “similarly significant effect.”
So the rule isn’t “no AI” — it’s “AI assists, humans decide.” That maps onto Plan Mode neatly. This post walks through an HR/recruiting agent that uses the right primitives.
What GDPR actually requires
Three intersecting obligations:
Article 5 (principles). Data minimization, purpose limitation, storage limitation. The agent can’t accumulate candidate data forever, and can only use it for the purpose disclosed.
Article 6 (lawful basis). Processing requires a lawful basis. For recruiting, usually consent or legitimate interest. Both require disclosure.
Article 22 (automated decision-making). Restricted. With limited exceptions (consent, contract necessity), candidates have the right to human review.
Plus state-level rules in the US (NYC AEDT, California’s various employment AI laws) and the EU AI Act’s high-risk classification for hiring AI.
The architecture
The HR/recruiting agent has to:
- Process candidate data under explicit consent scopes.
- Tag every candidate-derived assertion as
[pii]. - Run any hiring-affecting decision through Plan Mode → human approval.
- Enforce retention via decay.
- Provide a deletion path (GDPR Article 17, the right to erasure).
ONTO maps cleanly:
# Candidate ingestion.
onto.run_sync(
cv_text + "\n\n" + cover_letter,
user_id="candidate_4421",
consent_scopes=["memory:write", "candidate:intake"],
extract=True,
# Candidate-derived assertions get [pii] tag automatically via the
# extraction prompt config; you can also force tags.
)
# Recruiter requests a screening summary — read-only.
summary = onto.run_sync(
"Summarize candidate_4421 for the role.",
user_id="candidate_4421",
consent_scopes=["candidate:read", "tasks:write"],
mode="plan_only",
)
# Hiring decision is *never* autonomous. Plan Mode → recruiter decides.
decision_task = next(t for t in summary.tasks if t.tool == "propose_hire_decision")
recruiter_choice = recruiter_ui.review(decision_task) # advance / reject / interview
# Recruiter's decision is logged with their identity, scopes, and reasoning.
onto.audit.record_decision(
candidate_id="candidate_4421",
decided_by="recruiter_williams",
decision=recruiter_choice,
plan_id=summary.plan_id,
)
The audit log answers Article 22’s “was this fully automated” question with “no — see the decision row signed by recruiter_williams.”
The bias question
Algorithmic bias is the other major HR AI concern. Most major AI hiring guidance (NYC AEDT, EEOC) requires bias audits.
ONTO doesn’t run the audit for you. What it does:
- Stable input set. The eval framework lets you run the agent against a curated set of candidate profiles and compare outcomes by protected class.
- Reproducible runs. Pinned model versions, recorded extraction prompts, deterministic temperature settings. Re-running the eval next quarter is meaningful.
- Per-protected-class output tracking. Custom guardrails can flag outputs that pattern-match exclusionary language.
The audit is your responsibility. The primitives that make the audit possible are the SDK’s.
Retention and erasure
GDPR Article 17 gives candidates the right to erasure (“right to be forgotten”). Article 5 requires storage limitation regardless of request.
ONTO’s decay curves attach to assertions. Candidate-tagged data gets a retention policy:
# In your decay config
DECAY_POLICIES = {
"candidate_unsuccessful": Decay(
type="absolute",
ttl_days=180, # 6 months for unsuccessful candidates
on_expiry="purge",
),
"candidate_hired": Decay(
type="absolute",
ttl_days=365 * 7, # 7 years for hired (varies by jurisdiction)
on_expiry="purge",
),
}
A nightly purge job removes expired assertions. The audit log records the purge so you can demonstrate compliance.
For Article 17 requests, ONTO exposes a purge_user(user_id) API that removes all assertions for the user, with a confirmation row in the audit log.
The consent disclosure
Candidates need to know:
- What data is collected.
- What it’s used for.
- Who can access it.
- How long it’s kept.
- Whether automated decisions are involved (if so, the logic and significance).
- Their rights (access, rectification, erasure, portability).
This is mostly a privacy notice question, not an SDK question. What the SDK provides is the data flow — which lets you write an accurate privacy notice instead of a generic one. Your privacy notice and your code should match; the audit log proves they match.
The on-prem advantage
For EU candidates especially, the simplest privacy story is “data never leaves the EU.” Hosted models with US-based providers add data-transfer complexity (Schrems II, the EU-US Data Privacy Framework, SCCs).
Self-hosted open-weight models in an EU data center sidestep this. ONTO’s default deployment supports this directly — set up Postgres in Frankfurt, run Ollama in the same region, configure ONTO to use them. No data transfer occurs.
For US-based recruiting that also processes EU candidates, the on-prem option is operationally cleaner than maintaining DPF certifications.
The recruiter UX
The agent shouldn’t replace recruiters. It should accelerate them. The right UX:
- Auto-screening: agent reads CV + cover letter, surfaces top candidates with rationale.
- Recruiter reviews and decides.
- Agent drafts outreach emails, interview questions, decision rationales.
- Recruiter reviews, edits, sends.
Plan Mode supports all of this. The recruiter sees what the agent proposes, the rationale, and the underlying assertions. They approve or modify.
The agent’s value is velocity, not autonomy. Same number of hires, same quality bar, less recruiter time per hire.
The bias-rate metric
A concrete suggestion: track the bias rate of your AI agent the same way you track its accuracy. Set up:
- A held-out set of candidate profiles annotated with protected class (where allowed).
- A monthly eval that measures advancement rate by class.
- An alert when the rate drift exceeds threshold.
This is a CI job using ONTO’s eval framework. It’s not the only thing you need (a real bias audit is more involved), but it’s the kind of ongoing monitoring that catches regressions early.
What still feels hard
Two areas where the primitives help but don’t solve:
- Disparate impact in the underlying model. If the LLM is biased on its priors, your agent inherits it. Open-weight models you can fine-tune help; pre-deployment evals are mandatory.
- Hot-button decisions like accommodations or terminations. These shouldn’t touch an autonomous loop at all. Plan Mode here means “agent drafts; legal + HR review heavily.”
A summary
HR AI under GDPR works when:
- Every candidate-derived assertion is
[pii]tagged. - Retention curves enforce data minimization.
- Plan Mode keeps the decision human even when the drafting is AI.
- Bias monitoring runs continuously in CI.
- Privacy notices match the audit log.
The SDK provides the primitives that make all of this concrete. The policy and the audits are your responsibility — and they’re a lot easier to write against a system that has them built in.
The features page walks through consent scopes and Plan Mode in detail. The comparison page covers how other SDKs handle this.
Frequently asked questions
Does GDPR forbid AI in hiring?
No, but Article 22 restricts purely automated decisions about candidates. A fully autonomous AI hiring decision (no human in the loop) is high-risk. Plan Mode + human approval keeps the decision authority human.
How long can I keep candidate data?
Per GDPR Article 5, no longer than necessary for the purpose. Most jurisdictions and most companies set 6-24 months for unsuccessful candidates. Retention has to be enforced — ONTO's decay curves attach to tagged assertions.
Can the model see CVs containing personal data from EU candidates?
Yes, with a lawful basis (usually consent or legitimate interest), appropriate disclosure in the privacy notice, and a data-protection impact assessment if you're using automated profiling. Self-hosted models simplify the data-processor relationship.
The agent SDK where humans drive the state.
Plan Mode, typed memory, per-call consent scopes, and open-weight defaults. Open source under MIT or Apache-2.0.