2026-04-19·14 min read·

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:

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:

  1. Public interest — the processing serves a genuine public interest, not mere curiosity
  2. Reasonable belief — the processor reasonably believes the publication is in the public interest
  3. 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:

ProcessingDefault GDPRArt.85 Exemption
Publishing person's name in news storyRequires legal basisPublic interest exemption
Storing leaked documentsMinimisation + retention limitsMay be derogated
Rejecting subject access requestMust respond within 30 daysMay be derogated during investigation
Retaining historical news archiveStorage limitation appliesJournalism 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

JurisdictionFOI FrameworkGDPR/FOI Reconciliation
EU institutionsRegulation 1049/2001Case-by-case balancing required
GermanyIFG (Informationsfreiheitsgesetz)Data protection prevails for private persons unless public interest
SwedenTryckfrihetsförordningen (TF)World's oldest FOI law (1766); strong disclosure presumption
NetherlandsWob/Woo (2022)Privacy interests must be weighed per-document
IrelandFOI Act 2014DPC 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:

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:

  1. Determine jurisdiction — which Member State's law applies (usually where data subject resides)
  2. Check permitted purposes — national law lists specific allowed use cases
  3. Encrypt at rest and in transit — universally required across EU
  4. Mask in UIs — show only last 4 digits in admin panels, logs, error messages
  5. Separate storage — do not store national IDs in the same table as general profile data
  6. Audit log access — every read of a national ID field should be logged with user + purpose
  7. 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:

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:

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

CountryKey LawNotable Rules
Germany§26 BDSGStrict purpose limitation; works council rights; no performance monitoring without WC consent
FranceCode du travail Art.L2315-87 + CNIL guidanceCSE (employee representatives) must be consulted on monitoring systems
NetherlandsWet op de ondernemingsraden (WOR) Art.27Works council consent required for HR system changes
SwedenLag (2022:912) + collective agreementsMuch governed by sectoral collective agreements, not law
DenmarkAnsættelsesbevisloven + databeskyttelseslovenIndividual 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:

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

ArticleSubjectDeveloper Action
Art.85Journalism / expressionBuild press-exemption documentation module; apply proportionality test per-country
Art.86Public document accessCheck FOI obligations in target jurisdiction; reconcile with GDPR on disclosure
Art.87National identification numbersEncrypt, mask, audit-log; check national permitted purposes
Art.88Employment processingNo consent basis; role-based access; works council obligations in DE/FR/NL
Art.89Research / archiving / statisticsSee Post #443
Art.90Secrecy obligationsLawyers, doctors, journalists: professional secrecy may override GDPR disclosure
Art.91Church data processingReligious 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:

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