2026-04-17·19 min read·

DORA Art.36–39: Joint Examination Teams, Investigation Procedures, On-Site Inspection Protocols, and ESA Enforcement Cooperation (2026)

Post #412 in the sota.io EU Cyber Compliance Series

DORA Art.32–35 established what the Lead Overseer can do — request documents, run general investigations, conduct on-site inspections, and instruct NCAs to enforce findings against financial entities. Articles 36–39 answer the next question: how is each of these powers actually exercised in practice?

The procedural layer matters enormously for ICT providers. Knowing that the Lead Overseer can send an inspection team is different from knowing how much notice you get, who exactly arrives at your door, what they are legally entitled to copy, and what obligations your employees have when interviewed. Art.36–39 provides all of this, plus special rules for intragroup ICT arrangements that many cloud-native financial groups will encounter.

This guide covers:


Art.36: Conditions for General Investigations

Art.33(2)(b) granted the Lead Overseer the power to conduct general investigations of CTPPs. Art.36 operationalises that power by specifying exactly how general investigations proceed — the procedural rules that both protect CTPPs and ensure the Lead Overseer can fulfil its mandate.

Notification Requirements

The Lead Overseer must notify the CTPP before launching a general investigation. The notification must state:

  1. The investigation subject — which aspect of the CTPP's ICT risk management is under review
  2. The legal basis — reference to Art.33(2)(b) and the specific concern (e.g., identified gaps in incident detection controls)
  3. The requested documentation — categories of documents to be produced
  4. The response deadline — minimum 10 working days from notification

The 10-day minimum is a floor, not a ceiling. For complex investigations requiring extraction of multi-year audit trails or configuration management databases, the Lead Overseer typically negotiates a longer production timeline. CTPPs that proactively propose a longer reasonable deadline are better positioned than those that attempt to delay by formal objection — the Lead Overseer can shorten deadlines when it determines urgent circumstances exist.

Document Production Obligations

During a general investigation, CTPPs are legally obligated to produce:

CategoryExamplesFormat
Governance recordsBoard resolutions on ICT risk, CISO reporting lines, audit committee minutesPDF/certified copy
Operational recordsChange management logs, patch deployment records, vulnerability scan resultsOriginal system export
Incident recordsAll ICT-related incidents above notification thresholds since designationStructured export per Art.20/21 templates
Contractual recordsSub-outsourcing agreements, cloud provider contracts, SLAsPDF
Audit recordsInternal and external ICT audit reports, penetration test results, remediation trackingPDF
Financial recordsExpenditure on ICT security controls (relevant to fee calculation verification)Accountancy export

The Lead Overseer can request originals or certified copies. Where documents exist only in electronic form, the CTPP must produce them in a readable format specified by the Lead Overseer — "our ITSM doesn't export that way" is not a valid excuse and constitutes obstruction.

Staff Interview Rights

Art.36 authorises the Lead Overseer to interview any employee of the CTPP (or any person associated with its ICT operations) during a general investigation. Key procedural rules:

Interview subject rights:

CTPP obligations:

Professional secrecy: Art.36 explicitly extends the professional secrecy obligations that apply to the Lead Overseer itself to all information received during investigations. The Lead Overseer cannot disclose CTPP-specific investigation findings to third parties (including other CTPPs or their financial entity clients) except through the formal Art.35 NCA notification channel.

from dataclasses import dataclass, field
from datetime import date, timedelta
from typing import Optional
from enum import Enum

class InvestigationStatus(Enum):
    NOTIFIED = "notified"
    DOCUMENTS_PENDING = "documents_pending"
    DOCUMENTS_PRODUCED = "documents_produced"
    INTERVIEWS_SCHEDULED = "interviews_scheduled"
    INTERVIEWS_COMPLETE = "interviews_complete"
    LEAD_OVERSEER_REVIEW = "lead_overseer_review"
    FINDINGS_ISSUED = "findings_issued"
    REMEDIATION = "remediation"
    CLOSED = "closed"

@dataclass
class GeneralInvestigationRecord:
    investigation_id: str
    notification_date: date
    subject: str
    legal_basis: str = "DORA Art.33(2)(b) + Art.36"
    document_deadline: Optional[date] = None
    status: InvestigationStatus = InvestigationStatus.NOTIFIED
    documents_produced: list[str] = field(default_factory=list)
    interviews_scheduled: list[dict] = field(default_factory=list)
    legal_counsel_notified: bool = False
    obstruction_flags: list[str] = field(default_factory=list)

    def __post_init__(self):
        if self.document_deadline is None:
            # Default: 10 working days from notification
            self.document_deadline = self.notification_date + timedelta(days=14)

    def days_to_deadline(self) -> int:
        return (self.document_deadline - date.today()).days

    def compliance_status(self) -> dict:
        return {
            "investigation_id": self.investigation_id,
            "days_to_document_deadline": self.days_to_deadline(),
            "documents_outstanding": self.status in [
                InvestigationStatus.NOTIFIED,
                InvestigationStatus.DOCUMENTS_PENDING
            ],
            "legal_counsel_engaged": self.legal_counsel_notified,
            "obstruction_risk": len(self.obstruction_flags) > 0,
            "obstruction_flags": self.obstruction_flags,
        }

    def record_document_production(self, category: str) -> None:
        self.documents_produced.append(category)
        if len(self.documents_produced) >= 4:
            self.status = InvestigationStatus.DOCUMENTS_PRODUCED

    def flag_obstruction_risk(self, reason: str) -> None:
        """Record potential obstruction — consult legal counsel before not producing."""
        self.obstruction_flags.append(reason)

Art.37: Joint Examination Teams and On-Site Inspection Protocols

Art.37 governs the Lead Overseer's most intrusive enforcement instrument: the Joint Examination Team (JET) on-site inspection. While Art.36 investigations proceed primarily through document exchange and interviews, JET inspections involve the Lead Overseer's staff physically entering CTPP premises — data centres, offices, or operations centres — to review systems and records in situ.

JET Composition

A JET is assembled by the Lead Overseer for a specific inspection assignment. JET composition is not fixed; it varies based on the inspection subject:

RoleAppointmentNumber
JET LeaderLead Overseer staff (typically Head of Oversight or delegate)1
ESA Technical ExpertsLead Overseer staff with relevant domain expertise (cloud, incident response, cryptography)2–5
NCA RepresentativesStaff from NCAs of Member States where the CTPP serves financial entities1–3 per relevant NCA
Seconded SpecialistsNational cybersecurity authorities (ENISA liaison), external technical expertsAs needed

The JET Leader is the single decision-maker on site. They have authority to:

NCA participation in JETs is mandatory for inspections of CTPPs active in that NCA's jurisdiction. An NCA that fails to provide a representative does not block the inspection — the JET proceeds — but the NCA loses the right to object to inspection findings based on procedural grounds.

Notice Requirements: Announced vs. Unannounced

DORA Art.33(2)(c) granted the Lead Overseer the right to conduct both announced and unannounced on-site inspections. Art.37 specifies the procedural requirements for each:

Announced inspections (standard):

Unannounced inspections:

Practical implication for CTPPs: The 48-hour window is not preparation time for compliance — it is confirmation time for logistics. CTPPs that use the 48-hour window to modify systems or destroy records face severe sanctions under Art.42, including daily penalties of up to 1% of total annual worldwide turnover.

Admission and Access Rights

During an on-site inspection, the JET has the legal right to:

  1. Full premises access — all locations listed in the inspection warrant, including co-location facilities used by the CTPP (the CTPP must facilitate access even to third-party-operated infrastructure)
  2. System access — read access to any ICT system, log database, configuration management database (CMDB), or audit trail relevant to the inspection scope
  3. Document copying — certified copies of any document encountered, including documents not originally requested
  4. Seal and preserve — the JET Leader can request that specific system states be preserved (e.g., freeze a log rotation process) for the duration of the inspection
  5. Staff access — the right to interview any staff member on-site during inspection hours without prior scheduling

What the JET cannot do:

from dataclasses import dataclass, field
from datetime import date, datetime
from typing import Optional
from enum import Enum

class InspectionType(Enum):
    ANNOUNCED = "announced"
    UNANNOUNCED = "unannounced"

class InspectionReadiness(Enum):
    NOT_READY = "not_ready"
    PARTIAL = "partial"
    READY = "ready"
    INSPECTION_ACTIVE = "inspection_active"
    POST_INSPECTION = "post_inspection"

@dataclass
class JETMember:
    name: str
    role: str           # "leader", "esa_technical", "nca_representative", "specialist"
    organisation: str   # EBA/ESMA/EIOPA/NCA name
    expertise: list[str] = field(default_factory=list)

@dataclass
class OnSiteInspectionRecord:
    inspection_id: str
    inspection_type: InspectionType
    notification_date: Optional[date]
    inspection_start: Optional[date]
    inspection_scope: list[str] = field(default_factory=list)
    jet_members: list[JETMember] = field(default_factory=list)
    locations: list[str] = field(default_factory=list)
    liaison_designated: bool = False
    legal_counsel_notified: bool = False
    readiness: InspectionReadiness = InspectionReadiness.NOT_READY
    findings: list[dict] = field(default_factory=list)
    seals_requested: list[str] = field(default_factory=list)

    def hours_until_inspection(self) -> Optional[float]:
        if self.inspection_start is None:
            return None
        now = datetime.now().date()
        delta = self.inspection_start - now
        return delta.days * 24

    def is_within_48h_window(self) -> bool:
        h = self.hours_until_inspection()
        return h is not None and 0 <= h <= 48

    def readiness_report(self) -> dict:
        checks = {
            "liaison_designated": self.liaison_designated,
            "legal_counsel_notified": self.legal_counsel_notified,
            "locations_confirmed": len(self.locations) > 0,
            "jet_composition_known": len(self.jet_members) > 0,
            "scope_documented": len(self.inspection_scope) > 0,
        }
        passed = sum(checks.values())
        return {
            "inspection_id": self.inspection_id,
            "type": self.inspection_type.value,
            "hours_until_inspection": self.hours_until_inspection(),
            "readiness_score": passed / len(checks),
            "readiness_checks": checks,
            "critical_gaps": [k for k, v in checks.items() if not v],
        }

    def record_finding(self, finding_type: str, severity: str, description: str) -> None:
        self.findings.append({
            "type": finding_type,
            "severity": severity,
            "description": description,
            "recorded_at": datetime.now().isoformat(),
        })

class DORAJETManager:
    def __init__(self):
        self.investigations: dict[str, GeneralInvestigationRecord] = {}
        self.inspections: dict[str, OnSiteInspectionRecord] = {}

    def register_investigation(self, record: GeneralInvestigationRecord) -> None:
        self.investigations[record.investigation_id] = record

    def register_inspection(self, record: OnSiteInspectionRecord) -> None:
        self.inspections[record.inspection_id] = record

    def active_investigations(self) -> list[GeneralInvestigationRecord]:
        terminal = {InvestigationStatus.CLOSED}
        return [i for i in self.investigations.values() if i.status not in terminal]

    def upcoming_inspections(self, within_days: int = 7) -> list[OnSiteInspectionRecord]:
        result = []
        for insp in self.inspections.values():
            hours = insp.hours_until_inspection()
            if hours is not None and 0 <= hours <= within_days * 24:
                result.append(insp)
        return result

    def compliance_dashboard(self) -> dict:
        active_inv = self.active_investigations()
        upcoming_insp = self.upcoming_inspections()
        critical_deadlines = [
            inv for inv in active_inv
            if inv.days_to_deadline() <= 3
        ]
        return {
            "active_investigations": len(active_inv),
            "upcoming_inspections_7d": len(upcoming_insp),
            "critical_document_deadlines": len(critical_deadlines),
            "investigations_with_obstruction_risk": len([
                i for i in active_inv if i.obstruction_flags
            ]),
            "inspection_readiness_scores": {
                insp.inspection_id: insp.readiness_report()["readiness_score"]
                for insp in upcoming_insp
            },
        }

Art.38: Ongoing Oversight Cycle

Art.38 establishes the continuous supervision model that distinguishes DORA oversight from traditional one-off audits. After a CTPP is designated under Art.31, it enters a permanent oversight relationship with the Lead Overseer — not an annual inspection programme, but a continuous cycle of monitoring, assessment, and engagement.

The Oversight Cycle in Practice

The Art.38 ongoing oversight framework operates across four overlapping timescales:

Real-time (continuous):

Monthly:

Quarterly:

Annual:

Risk Scoring Framework

The Lead Overseer maintains a dynamic risk score for each designated CTPP. Art.38 does not specify a scoring methodology — ESAs have discretion to develop their own. In practice, the JOC has converged on a multi-dimensional framework that the ESAs apply consistently:

DimensionWeightInputs
Incident frequency and severity30%Art.19/20 notifications, CTPP breach disclosures
ICT risk management maturity25%Investigation findings, audit results, TLPT outcomes
Systemic relevance20%Number/type of financial entity clients, critical service concentration
Sub-outsourcing depth15%Tier-2/tier-3 chain length, geographic concentration
Regulatory track record10%History of cooperation, remediation timeliness, sanctions

A CTPP risk score above threshold triggers a formal investigation; a score below threshold reduces oversight intensity in the next annual plan cycle.

from dataclasses import dataclass, field
from datetime import date

@dataclass
class CTPPRiskScore:
    ctpp_id: str
    assessment_date: date
    incident_frequency_severity: float   # 0.0–1.0
    ict_risk_maturity: float             # 0.0–1.0 (1.0 = high maturity = low risk)
    systemic_relevance: float            # 0.0–1.0
    sub_outsourcing_depth: float         # 0.0–1.0 (1.0 = deep = high risk)
    regulatory_track_record: float      # 0.0–1.0 (1.0 = excellent track record = low risk)

    WEIGHTS = {
        "incident": 0.30,
        "maturity": 0.25,
        "systemic": 0.20,
        "sub_outsourcing": 0.15,
        "track_record": 0.10,
    }

    def composite_risk_score(self) -> float:
        """Higher score = higher risk. Maturity and track_record are inverted."""
        raw = (
            self.incident_frequency_severity * self.WEIGHTS["incident"]
            + (1 - self.ict_risk_maturity) * self.WEIGHTS["maturity"]
            + self.systemic_relevance * self.WEIGHTS["systemic"]
            + self.sub_outsourcing_depth * self.WEIGHTS["sub_outsourcing"]
            + (1 - self.regulatory_track_record) * self.WEIGHTS["track_record"]
        )
        return round(raw, 3)

    def oversight_intensity(self) -> str:
        score = self.composite_risk_score()
        if score >= 0.70:
            return "HIGH — formal investigation likely; intensive oversight plan"
        elif score >= 0.45:
            return "MEDIUM — quarterly engagement; targeted document review"
        else:
            return "LOW — annual review cycle; standard oversight plan"

    def risk_report(self) -> dict:
        return {
            "ctpp_id": self.ctpp_id,
            "assessment_date": str(self.assessment_date),
            "composite_risk_score": self.composite_risk_score(),
            "oversight_intensity": self.oversight_intensity(),
            "dimension_scores": {
                "incident_frequency_severity": self.incident_frequency_severity,
                "ict_risk_maturity": self.ict_risk_maturity,
                "systemic_relevance": self.systemic_relevance,
                "sub_outsourcing_depth": self.sub_outsourcing_depth,
                "regulatory_track_record": self.regulatory_track_record,
            },
        }

Art.39: Oversight of ICT Intragroup Service Providers

Art.39 addresses a structural reality of large financial groups: their ICT infrastructure is often provided by a dedicated entity within the same corporate group, not by an independent third party. A bank may receive cloud services from a group subsidiary; an insurer may consume incident response services from a group-level security operations centre.

The Intragroup Distinction

Art.39 provides a qualified exemption from the full CTPP designation framework for ICT intragroup service providers — but this exemption is conditional, not automatic. Three conditions must be met simultaneously:

Condition 1: Same prudential supervision group The ICT provider must be part of the same prudential consolidation group as the financial entity. A subsidiary in a third country counts if it falls within the group's consolidated supervision perimeter.

Condition 2: No external service provision The intragroup provider must provide ICT services exclusively (or predominantly) to entities within the group. If it has significant external revenue from providing equivalent services to non-group financial entities, the exemption does not apply.

Condition 3: Robust intragroup governance The financial entity (as "intragroup ICT service user") must demonstrate that:

Why Intragroup Still Requires Attention

Even where Art.39 applies, several DORA obligations continue to apply without exemption:

ObligationApplies to Intragroup?Notes
Art.28–30 contractual provisionsYes — partiallyInternal SLAs must meet Art.30 minimum standards
Art.30(4) sub-outsourcing rulesYes — fullyIntragroup providers outsourcing to external parties get no exemption
Art.6–16 ICT risk frameworkYes — fullyThe financial entity remains fully responsible for intragroup ICT risk
Incident reporting Art.19–20Yes — fullyIntragroup-caused incidents are reported identically to third-party incidents
Register of information Art.28(3)Yes — fullyIntragroup ICT arrangements must appear in the register
CTPP designation + oversight feesNo — exemptThe intragroup provider is not designated; no oversight fee applies
JET inspectionsNo — exempt unless sub-outsourcing appliesLead Overseer oversight applies only to designated CTPPs
from dataclasses import dataclass, field
from enum import Enum

class IntragroupStatus(Enum):
    FULLY_EXEMPT = "fully_exempt"
    PARTIALLY_EXEMPT = "partially_exempt"
    NOT_EXEMPT = "not_exempt"
    ASSESSMENT_NEEDED = "assessment_needed"

@dataclass
class IntragroupICTProvider:
    provider_name: str
    parent_group: str
    same_consolidation_group: bool
    external_revenue_pct: float  # % of revenue from non-group entities
    external_revenue_threshold: float = 0.10  # >10% = significant external provision

    def exemption_status(self) -> IntragroupStatus:
        if not self.same_consolidation_group:
            return IntragroupStatus.NOT_EXEMPT
        if self.external_revenue_pct > self.external_revenue_threshold:
            return IntragroupStatus.NOT_EXEMPT
        return IntragroupStatus.FULLY_EXEMPT

    def remaining_obligations(self) -> list[str]:
        status = self.exemption_status()
        if status == IntragroupStatus.NOT_EXEMPT:
            return [
                "Full CTPP designation process applies",
                "Oversight fees under CDR 2024/2819",
                "JET inspections possible",
                "All Art.28–35 obligations",
            ]
        return [
            "Art.30 contractual provisions (internal SLAs must meet minimum standards)",
            "Art.30(4) sub-outsourcing rules for external sub-contractors",
            "Art.6–16 ICT risk management (full — applies to the financial entity)",
            "Art.19–20 incident reporting (intragroup incidents not exempt)",
            "Art.28(3) register of information (intragroup arrangements must appear)",
        ]

    def compliance_summary(self) -> dict:
        return {
            "provider": self.provider_name,
            "group": self.parent_group,
            "exemption_status": self.exemption_status().value,
            "external_revenue_pct": self.external_revenue_pct,
            "external_revenue_threshold": self.external_revenue_threshold,
            "remaining_obligations": self.remaining_obligations(),
        }

Common Failure Patterns in Art.36–39 Compliance

1. Treating investigations as audits CTPPs that apply their standard external audit response playbook to Lead Overseer investigations typically underproduce documentation (auditors get summaries; Lead Overseers get originals) and overproduce management commentary (investigation responses must be factual records, not narrative explanations).

2. Failing to designate an inspection liaison in time The 24-hour liaison designation requirement in announced inspections is frequently missed. Without a designated liaison, the JET has no single point of contact for access coordination, which the Lead Overseer treats as non-cooperation evidence.

3. Using the 48-hour window for system changes Any system modification made in the 48-hour window between inspection notice and inspection start creates a presumption of evidence manipulation. System changes during this window require contemporaneous documentation (change request, change approval, business justification) that can be reviewed by the JET.

4. Assuming intragroup exemption without assessment Art.39 exemption is not self-executing. Many financial groups assume their intragroup ICT providers are automatically exempt — but if those providers have begun selling services outside the group (SaaS revenue, white-label partnerships), the exemption threshold may have been exceeded without a formal re-assessment.

5. Missing sub-outsourcing disclosure The ongoing oversight cycle requires notification of new sub-outsourcing arrangements. Groups that treat their intragroup provider's cloud purchasing decisions as administrative procurement (not regulatory disclosure) risk finding that their sub-outsourcing register is incomplete at inspection.

6. Professional secrecy mismatch CTPPs that receive investigation notifications sometimes forward them to their financial entity clients for "transparency." This violates the professional secrecy provisions of Art.36 — the CTPP is legally required to treat investigation notifications as confidential.


DORA × NIS2 × GDPR Cross-Mapping: Investigation and Inspection Powers

DimensionDORA Art.36–39NIS2 Art.32–35GDPR Art.58
Investigation triggerLead Overseer discretion (risk-based)NCA discretion (incident-driven or proactive)Supervisory authority (complaint or own-initiative)
Document request deadline10 working days (minimum)NIS2 silent (national law determines)No specific minimum
On-site inspection notice48h (announced); 0h (unannounced)NIS2 silent (national law)No advance notice requirement in text
Inspection team compositionJET (multi-ESA + NCA + specialists)NCA staff onlyDPA staff only
Staff interview rightsYes — written record, legal counsel rightYes — national procedural law appliesYes — national procedural law applies
Seal/preserve rightYes (exceptional, JOC approval)NIS2 silentNo specific seal power
Intragroup provisionArt.39 qualified exemptionNo equivalent (NIS2 covers entities, not CTPPs)No equivalent
Ongoing oversight cycleYes — continuous (Art.38)Periodic reviews onlyNo continuous monitoring mandate
Professional secrecyExplicit (Art.36)Explicit (NIS2 Art.37)Explicit (GDPR Art.54(2))

Relationship to the Broader DORA Chapter V Chain

Articles 36–39 fit within the full oversight chain as the procedural execution layer:

Art.31:  WHO is supervised (CTPP designation criteria)
Art.32:  HOW oversight is structured (JOC, Lead Overseer, JET framework)
Art.33:  WHAT the Lead Overseer can do (powers granted)
Art.34:  WHERE powers apply (including non-EEA operations)
Art.35:  WHAT happens after findings (NCA follow-up, entity obligations)
Art.36:  HOW investigations proceed (notification, document production, interviews) ←
Art.37:  HOW JET inspections are conducted (composition, access, protocols) ←
Art.38:  HOW oversight continues (ongoing cycle, risk scoring) ←
Art.39:  WHEN intragroup providers get different rules ←
Art.40+: WHAT remediation looks like (findings, measures, termination, sanctions)

Understanding Art.36–39 as the execution layer clarifies why they matter operationally even for CTPPs that have strong Art.33 awareness. Knowing the Lead Overseer can inspect is not the same as being ready for a JET to arrive at 09:00 with 48 hours' notice.


28-Item Procedural Compliance Checklist

Use this checklist to assess readiness for Art.36–39 enforcement actions.

General Investigation Readiness (Art.36)

JET On-Site Inspection Readiness (Art.37)

Ongoing Oversight Cycle (Art.38)

Intragroup Assessment (Art.39)

See Also

See Also