GDPR Art.85–88 Specific Processing Situations: Journalism, Employment & National IDs — Developer Guide (2026)
Post #450 in the sota.io EU Cyber Compliance Series
GDPR Chapter IX (Articles 85–91) carves out space for Member States to adapt data protection rules to specific societal contexts. These are not loopholes — they are structural accommodations where the GDPR's default rules would make certain legitimate activities impossible: investigative journalism would be paralysed by right-to-erasure demands; HR software would be illegal without employment-specific rules; national ID systems would be unenforceable without standardised handling requirements.
For developers, Chapter IX is where "it depends on the country" becomes the correct answer — and where ignoring national law creates liability.
Chapter IX Map
Chapter IX — Provisions Relating to Specific Processing Situations
Art.85 — Processing and freedom of expression and information
Art.86 — Processing and public access to official documents
Art.87 — Processing of the national identification number
Art.88 — Processing in the context of employment
Art.89 — Safeguards and derogations for archiving, research, statistics (DONE — Post #443)
Art.90 — Obligations of secrecy
Art.91 — Existing data protection rules of churches and religious associations
Articles 85–88 share a common architecture: the GDPR sets the outer boundary, Member States fill in the specifics via national law, and you — as the developer — must implement whatever the applicable national law requires.
Art.85 — Freedom of Expression and Journalism
What Art.85 Does
Art.85(1) requires every Member State to reconcile the right to data protection with the right to freedom of expression and information — including processing for journalistic purposes and for purposes of academic, artistic, or literary expression.
Art.85(2) is the derogation engine: Member States shall provide exemptions or derogations from Chapters II (principles), III (data subject rights), IV (controller/processor obligations), V (international transfers), VI (supervisory authorities), VII (cooperation), and IX (specific situations) insofar as they are necessary to reconcile privacy with expression.
This means a newspaper's CMS is potentially exempt from:
- Art.5 principles (purpose limitation, data minimisation) — to the extent incompatible with publication
- Art.15-22 data subject rights (access, erasure, restriction) — because granting erasure to a public figure would kill the story
- Art.30 Records of Processing (RoPA) — in some Member States' implementations
The Journalism Exemption Test
All Art.85 exemptions require proportionality — the derogation must be "necessary" for the journalistic/expressive purpose. Courts across the EU have consistently required three elements:
- Public interest — the processing serves a genuine public interest, not mere curiosity
- Reasonable belief — the processor reasonably believes the publication is in the public interest
- Incompatibility — GDPR compliance would be incompatible with the journalistic purpose
from dataclasses import dataclass
from enum import Enum
from typing import Optional
class ExpressionContext(Enum):
JOURNALISM = "journalism"
ACADEMIC = "academic"
ARTISTIC = "artistic"
LITERARY = "literary"
@dataclass
class Art85ExemptionAssessment:
"""
GDPR Art.85(2) journalism/expression exemption assessment.
Exemption applies only if all three conditions are met.
National law determines which specific GDPR provisions are derogated.
"""
context: ExpressionContext
public_interest: bool # Serves genuine public interest
reasonable_belief: bool # Reasonable belief publication is justified
incompatibility: bool # GDPR compliance would frustrate the purpose
member_state: str # Which national press/expression law applies
specific_derogations: list[str] # Which GDPR articles are derogated
@property
def exemption_applies(self) -> bool:
return self.public_interest and self.reasonable_belief and self.incompatibility
def get_remaining_obligations(self) -> list[str]:
"""Art.85 does NOT exempt everything — these always remain."""
return [
"Art.5(1)(f) — data security (integrity/confidentiality)",
"Art.32 — technical security measures",
"Art.33/34 — breach notification (unless expressly derogated)",
"Source protection obligations under national press law",
]
# Germany: Rundfunkdatenschutzgesetz (§9a BDSG equivalent)
# France: Loi Informatique et Libertés Art.80
# Netherlands: Wet bescherming persoonsgegevens Art.43
# UK (post-Brexit): UK GDPR Art.85 + Data Protection Act 2018 s.26
NATIONAL_PRESS_DEROGATIONS: dict[str, list[str]] = {
"DE": ["Art.9 special categories for investigative journalism", "Art.15-22 subject rights (case-by-case)"],
"FR": ["Art.9(2)(g) broader public-interest basis", "Art.17 erasure right limited for archives"],
"NL": ["Art.9(1) prohibition suspended for journalistic purposes", "Art.15-20 rights suspended during investigation"],
"SE": ["Broad press freedom constitution overrides GDPR in many cases", "RF/TF (Swedish constitutional press laws)"],
}
def assess_art85_exemption(
content_type: ExpressionContext,
serves_public_interest: bool,
country_code: str,
) -> Art85ExemptionAssessment:
derogations = NATIONAL_PRESS_DEROGATIONS.get(country_code, ["See national implementation"])
return Art85ExemptionAssessment(
context=content_type,
public_interest=serves_public_interest,
reasonable_belief=True, # Must document this assessment
incompatibility=True, # Must be assessed per-case
member_state=country_code,
specific_derogations=derogations,
)
Developer Implications
If you build a CMS, investigative journalism platform, or media archive:
| Processing | Default GDPR | Art.85 Exemption |
|---|---|---|
| Publishing person's name in news story | Requires legal basis | Public interest exemption |
| Storing leaked documents | Minimisation + retention limits | May be derogated |
| Rejecting subject access request | Must respond within 30 days | May be derogated during investigation |
| Retaining historical news archive | Storage limitation applies | Journalism exemption for archives |
Practice: Build a journalist-facing documentation module in your CMS. Editors must document (a) the public interest rationale, (b) why privacy intrusion is necessary, and (c) which Art.85 national law provision applies. This audit trail is your defence in regulatory proceedings.
Art.86 — Processing and Public Access to Official Documents
What Art.86 Does
Art.86 addresses the collision between GDPR and freedom of information (FOI) / public access to documents laws. EU institutions, national governments, and local authorities hold enormous volumes of personal data in public registers. FOI laws often require disclosure; GDPR might restrict it.
Art.86 permits disclosure of personal data in official documents if reconciling the GDPR with public access law requires it. Member States must balance the two — neither GDPR nor FOI automatically wins.
EU and National Implementations
| Jurisdiction | FOI Framework | GDPR/FOI Reconciliation |
|---|---|---|
| EU institutions | Regulation 1049/2001 | Case-by-case balancing required |
| Germany | IFG (Informationsfreiheitsgesetz) | Data protection prevails for private persons unless public interest |
| Sweden | Tryckfrihetsförordningen (TF) | World's oldest FOI law (1766); strong disclosure presumption |
| Netherlands | Wob/Woo (2022) | Privacy interests must be weighed per-document |
| Ireland | FOI Act 2014 | DPC guidance governs reconciliation |
Developer scenario: You're building a public contracts database that pulls from government procurement APIs. Some records contain personal data of sole traders (their names appear in contracts). Art.86 means: if the government published those records under national FOI law, you may re-process them — but you should apply the same access rules the original authority applied.
Art.87 — Processing of the National Identification Number
What Art.87 Does
Art.87 is brief but consequential: Member States shall determine the specific conditions under which national identification numbers (and "other identifiers of general application") may be processed, ensuring "appropriate safeguards" are in place.
This is not a blanket permission — it means national law governs when national IDs can be used, and you cannot rely on general GDPR bases alone.
Why National IDs Get Special Treatment
National identification numbers are universal keys to a person's entire data footprint. Unlike a name or email, a national ID:
- Links records across government, banking, health, and tax systems
- Is immutable and non-replaceable (unlike a password)
- Creates aggregation risk far exceeding individual data points
- Is a prime target for identity theft and social engineering
from dataclasses import dataclass, field
from typing import Optional
@dataclass
class NationalIDConfig:
"""
Art.87 national ID handling configuration.
Each EU country has different rules — this maps them.
"""
country_code: str
id_type: str
governing_law: str
permitted_purposes: list[str]
storage_encryption_required: bool
display_masking_required: bool
cross_border_transfer_restricted: bool
retention_limit_years: Optional[int]
# Country-specific Art.87 configurations
NATIONAL_ID_RULES: dict[str, NationalIDConfig] = {
"DE": NationalIDConfig(
country_code="DE",
id_type="Steueridentifikationsnummer (Steuer-ID) / Personalausweisnummer",
governing_law="§18 BDSG — Bundesdatenschutzgesetz",
permitted_purposes=["tax", "social security", "public administration"],
storage_encryption_required=True,
display_masking_required=True,
cross_border_transfer_restricted=True,
retention_limit_years=10,
),
"FR": NationalIDConfig(
country_code="FR",
id_type="Numéro de Sécurité Sociale (NIR)",
governing_law="Loi Informatique et Libertés Art.29 / CNIL Authorization",
permitted_purposes=["health", "social security", "employment", "CNIL-authorised"],
storage_encryption_required=True,
display_masking_required=True,
cross_border_transfer_restricted=True,
retention_limit_years=5,
),
"NL": NationalIDConfig(
country_code="NL",
id_type="Burgerservicenummer (BSN)",
governing_law="Wet gebruik BSN (Wabb) / Wet bescherming persoonsgegevens",
permitted_purposes=["government", "healthcare", "insurance", "pension"],
storage_encryption_required=True,
display_masking_required=True,
cross_border_transfer_restricted=True,
retention_limit_years=7,
),
"SE": NationalIDConfig(
country_code="SE",
id_type="Personnummer",
governing_law="Lag (2022:912) om behandling av personuppgifter — Art.87 derogation",
permitted_purposes=["government", "banking", "healthcare", "commercial-with-justification"],
storage_encryption_required=True,
display_masking_required=False, # Personnummer widely used commercially in SE
cross_border_transfer_restricted=False, # Relatively permissive nationally
retention_limit_years=None, # Purpose-dependent
),
"FI": NationalIDConfig(
country_code="FI",
id_type="Henkilötunnus (HETU)",
governing_law="Tietosuojalaki (1050/2018) §29",
permitted_purposes=["public administration", "banking", "insurance", "healthcare"],
storage_encryption_required=True,
display_masking_required=True,
cross_border_transfer_restricted=True,
retention_limit_years=7,
),
"EE": NationalIDConfig(
country_code="EE",
id_type="Isikukood",
governing_law="Isikuandmete kaitse seadus (IKS) — Art.87 implementation",
permitted_purposes=["e-government", "digital-signing", "banking", "healthcare"],
storage_encryption_required=True,
display_masking_required=False, # Widely used in e-Estonia ecosystem
cross_border_transfer_restricted=False,
retention_limit_years=None,
),
}
def validate_national_id_processing(
country_code: str,
purpose: str,
will_transfer_abroad: bool = False,
) -> dict[str, bool | str]:
config = NATIONAL_ID_RULES.get(country_code)
if not config:
return {
"permitted": False,
"reason": f"No Art.87 mapping for {country_code} — check national law directly",
}
purpose_permitted = any(p in purpose.lower() for p in config.permitted_purposes)
transfer_blocked = will_transfer_abroad and config.cross_border_transfer_restricted
return {
"permitted": purpose_permitted and not transfer_blocked,
"governing_law": config.governing_law,
"encryption_required": config.storage_encryption_required,
"masking_required": config.display_masking_required,
"retention_years": config.retention_limit_years,
"transfer_restricted": config.cross_border_transfer_restricted,
}
Developer Checklist for National ID Processing
If your application collects or processes national ID numbers:
- Determine jurisdiction — which Member State's law applies (usually where data subject resides)
- Check permitted purposes — national law lists specific allowed use cases
- Encrypt at rest and in transit — universally required across EU
- Mask in UIs — show only last 4 digits in admin panels, logs, error messages
- Separate storage — do not store national IDs in the same table as general profile data
- Audit log access — every read of a national ID field should be logged with user + purpose
- Never log raw values — national IDs must never appear in application logs or crash reports
# Safe national ID handling pattern
import hashlib
import re
def mask_national_id(national_id: str, country: str = "DE") -> str:
"""Display-safe masking for national IDs. Never show full number in UI."""
cleaned = re.sub(r'[\s\-]', '', national_id)
if len(cleaned) <= 4:
return "****"
visible_chars = 4 if country in ("SE", "EE") else 3
return "*" * (len(cleaned) - visible_chars) + cleaned[-visible_chars:]
def create_national_id_token(national_id: str, salt: str) -> str:
"""
Pseudonymisation token for national IDs.
Use this token in application logic — store the mapping separately
with strict access controls per Art.87.
"""
return hashlib.sha256(f"{salt}:{national_id}".encode()).hexdigest()[:16]
Art.88 — Processing in the Context of Employment
What Art.88 Does
Art.88(1) permits Member States to provide more specific rules to ensure the protection of employee data in the employment context. This covers:
- Recruitment
- Employment contract performance
- Management, planning, and organisation of work
- Equality and diversity in the workplace
- Health and safety at work
- Protection of employer/customer/employee property
- Exercise of employment-related rights and benefits
- Termination of employment
Art.88(2) adds minimum requirements: national rules must include suitable and specific measures to safeguard employee dignity, legitimate interests, and fundamental rights — with particular attention to transparency and transfer limitations.
Why Employment Data Is Different
Employment creates a structural power imbalance: employees cannot freely consent to processing under GDPR because their employment depends on the relationship with the employer. This is why:
- Consent is not a valid legal basis for most employment processing in the EU (confirmed in EDPB Guidelines 05/2020 on consent)
- The correct legal bases are Art.6(1)(b) (contract performance), Art.6(1)(c) (legal obligation), and Art.6(1)(f) (legitimate interests — with careful balancing)
- Special category data (Art.9) requires explicit national employment law authorization, not just consent
from dataclasses import dataclass
from enum import Enum
from typing import Optional
class EmploymentDataCategory(Enum):
# Art.6 data — general processing
CONTACT = "contact" # Name, address, email, phone
COMPENSATION = "compensation" # Salary, benefits, bonuses
PERFORMANCE = "performance" # Reviews, KPIs, metrics
ATTENDANCE = "attendance" # Hours, leave, absences
TRAINING = "training" # Skills, certifications, courses
CONTRACTS = "contracts" # Employment agreements, NDAs
DISCIPLINARY = "disciplinary" # Warnings, investigations
# Art.9 data — special category
HEALTH = "health" # Sick days, disability, medical certificates
BIOMETRIC = "biometric" # Fingerprint/face for access control
TRADE_UNION = "trade_union" # Membership, activities
ETHNICITY = "ethnicity" # For diversity reporting (Art.88(1))
@dataclass
class EmploymentProcessingRule:
category: EmploymentDataCategory
gdpr_basis: str
national_law_required: bool
consent_valid: bool # Almost never for employment
retention_years: int
subject_rights_restrictions: list[str]
transfer_limitation: str
EMPLOYMENT_PROCESSING_RULES: dict[EmploymentDataCategory, EmploymentProcessingRule] = {
EmploymentDataCategory.COMPENSATION: EmploymentProcessingRule(
category=EmploymentDataCategory.COMPENSATION,
gdpr_basis="Art.6(1)(b) — contract performance + Art.6(1)(c) — legal obligation (tax)",
national_law_required=True, # Tax/labour law governs retention
consent_valid=False,
retention_years=10, # Tax retention requirements in most EU states
subject_rights_restrictions=["Art.17 erasure limited during employment"],
transfer_limitation="Within EEA only unless adequate safeguards (payroll processors)",
),
EmploymentDataCategory.HEALTH: EmploymentProcessingRule(
category=EmploymentDataCategory.HEALTH,
gdpr_basis="Art.9(2)(b) — employment law obligation + Art.9(2)(h) — occupational medicine",
national_law_required=True, # MUST be authorised by national employment law
consent_valid=False, # EDPB Guidelines 05/2020: no valid consent in employment
retention_years=3, # Typically 3 years post-employment; varies by country
subject_rights_restrictions=[
"Art.15 access: may be restricted for occupational medicine reports",
"Art.17 erasure: blocked during employment for legal compliance",
],
transfer_limitation="Cannot be transferred to line managers without medical necessity",
),
EmploymentDataCategory.BIOMETRIC: EmploymentProcessingRule(
category=EmploymentDataCategory.BIOMETRIC,
gdpr_basis="Art.9(2)(b) — only if national employment law explicitly permits",
national_law_required=True,
consent_valid=False, # Coercion risk in employment makes consent invalid
retention_years=0, # Delete immediately on employment end
subject_rights_restrictions=[],
transfer_limitation="No transfer — local processing only",
),
EmploymentDataCategory.DISCIPLINARY: EmploymentProcessingRule(
category=EmploymentDataCategory.DISCIPLINARY,
gdpr_basis="Art.6(1)(f) — legitimate interests (protecting company, other employees)",
national_law_required=True,
consent_valid=False,
retention_years=5, # Limitation periods for employment disputes
subject_rights_restrictions=[
"Art.15 access: may be restricted during active investigations",
"Art.21 objection: limited — legitimate interest documented",
],
transfer_limitation="Only to HR and legal — not to managers unless relevant",
),
EmploymentDataCategory.TRADE_UNION: EmploymentProcessingRule(
category=EmploymentDataCategory.TRADE_UNION,
gdpr_basis="Art.9(2)(b) — labour law OR Art.9(2)(d) — union's own processing of members",
national_law_required=True,
consent_valid=False, # Cannot condition employment on disclosure
retention_years=3,
subject_rights_restrictions=[],
transfer_limitation="Strictly no transfer to employer without union consent",
),
}
def get_employment_basis(
category: EmploymentDataCategory,
context: str = "standard",
) -> dict[str, str | bool | int]:
rule = EMPLOYMENT_PROCESSING_RULES.get(category)
if not rule:
return {
"basis": "Art.6(1)(b) — contract",
"national_law_required": False,
"consent_valid": False,
}
return {
"basis": rule.gdpr_basis,
"national_law_required": rule.national_law_required,
"consent_valid": rule.consent_valid,
"retention_years": rule.retention_years,
"transfer_restriction": rule.transfer_limitation,
}
National Art.88 Implementations
| Country | Key Law | Notable Rules |
|---|---|---|
| Germany | §26 BDSG | Strict purpose limitation; works council rights; no performance monitoring without WC consent |
| France | Code du travail Art.L2315-87 + CNIL guidance | CSE (employee representatives) must be consulted on monitoring systems |
| Netherlands | Wet op de ondernemingsraden (WOR) Art.27 | Works council consent required for HR system changes |
| Sweden | Lag (2022:912) + collective agreements | Much governed by sectoral collective agreements, not law |
| Denmark | Ansættelsesbevisloven + databeskyttelsesloven | Individual employment contract governs many aspects |
Building Art.88-Compliant HR Software
# HR data access control pattern (Art.88 + Art.5(1)(f))
from functools import wraps
from typing import Callable
ROLE_DATA_ACCESS = {
"hr_manager": [EmploymentDataCategory.COMPENSATION, EmploymentDataCategory.HEALTH,
EmploymentDataCategory.DISCIPLINARY, EmploymentDataCategory.PERFORMANCE],
"line_manager": [EmploymentDataCategory.PERFORMANCE, EmploymentDataCategory.ATTENDANCE,
EmploymentDataCategory.TRAINING],
"payroll": [EmploymentDataCategory.COMPENSATION, EmploymentDataCategory.ATTENDANCE],
"it_admin": [EmploymentDataCategory.CONTACT], # Minimal access principle
"employee_self": [EmploymentDataCategory.CONTACT, EmploymentDataCategory.COMPENSATION,
EmploymentDataCategory.PERFORMANCE, EmploymentDataCategory.ATTENDANCE,
EmploymentDataCategory.TRAINING], # Art.15 access right
}
def require_employment_data_access(category: EmploymentDataCategory) -> Callable:
"""Decorator enforcing Art.88 role-based access for employee data categories."""
def decorator(func: Callable) -> Callable:
@wraps(func)
def wrapper(*args, role: str = "unknown", **kwargs):
allowed = ROLE_DATA_ACCESS.get(role, [])
if category not in allowed:
raise PermissionError(
f"Role '{role}' cannot access {category.value} data. "
f"Art.88 requires need-to-know principle."
)
return func(*args, role=role, **kwargs)
return wrapper
return decorator
@require_employment_data_access(EmploymentDataCategory.HEALTH)
def get_employee_sick_leave(employee_id: str, *, role: str) -> dict:
# Only HR and occupational health can access this
...
The Monitoring Problem
Employee monitoring (emails, browsing, productivity tracking) is one of the most contested Art.88 areas:
- Germany: §26 BDSG + works council consent required. Keyloggers are illegal without explicit agreement. CCTV in workplaces requires works council approval.
- France: CNIL has fined companies for covert monitoring. Any monitoring system must be declared to employees + CSE beforehand.
- Netherlands: Proportionality test required. Background monitoring of all employees is generally prohibited.
If you build workforce analytics, productivity monitoring, or employee communication tools — works council consultation requirements are mandatory in DE/FR/NL before deployment. Build this into your enterprise sales process.
Chapter IX Summary Table
| Article | Subject | Developer Action |
|---|---|---|
| Art.85 | Journalism / expression | Build press-exemption documentation module; apply proportionality test per-country |
| Art.86 | Public document access | Check FOI obligations in target jurisdiction; reconcile with GDPR on disclosure |
| Art.87 | National identification numbers | Encrypt, mask, audit-log; check national permitted purposes |
| Art.88 | Employment processing | No consent basis; role-based access; works council obligations in DE/FR/NL |
| Art.89 | Research / archiving / statistics | See Post #443 |
| Art.90 | Secrecy obligations | Lawyers, doctors, journalists: professional secrecy may override GDPR disclosure |
| Art.91 | Church data processing | Religious organisations may apply own rules if "equivalent" to GDPR |
sota.io: EU Infrastructure for Chapter IX Compliance
Art.87 national ID processing and Art.88 employment data both demand:
- Data residency within the EU — national ID cross-border restrictions and employment law jurisdiction requirements are satisfied automatically
- No CLOUD Act exposure — US parent companies cannot comply with Art.88 transfer limitations without EU-only infrastructure
- Audit logging at infrastructure level — every access to sensitive employment or national ID data can be logged at the platform layer, not just the application layer
HR startups and identity platforms building in the EU need infrastructure that defaults to compliant — not infrastructure that requires compliance to be bolted on later.
See Also
- GDPR Art.9 — Special Category Data Processing — the health/biometric/trade-union basis that feeds into Art.88
- GDPR Art.89 — Research, Archiving & Statistics — the sister article covering academic/research derogations
- GDPR Art.32 — Security of Processing — encryption requirements that apply to national IDs and employment data
- GDPR Art.6 — Lawful Bases — why consent fails in employment contexts
- GDPR Art.66–76 EDPB Structure — the body that issues binding guidelines on Art.85–88 implementation