2026-04-18·15 min read·

GDPR Art.26: Joint Controllers — Shared Data Responsibility, Arrangements & Engineering Patterns (2026)

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

Art.26 addresses one of the least-understood liability structures in data protection law: joint controllership. When two organisations co-determine why and how data is processed, they do not get to split the GDPR burden arbitrarily — the regulation binds them both, in full, and data subjects can enforce rights against either party regardless of internal allocation agreements.

For developers and platform engineers this matters directly: embedded analytics pixels, shared infrastructure, partner APIs, co-branded products, and multi-tenant SaaS architectures frequently create joint controllership without the engineering teams realising it.


GDPR Chapter IV: Art.26 in Context

ArticleRolePrimary ObligationRelationship
Art.24Controller accountabilityImplement + demonstrate TOMsSole or joint
Art.25Privacy by DesignBuild data minimisation into architectureSole or joint
Art.26Joint ControllersArrange responsibilities; expose contact point to data subjectsTwo or more controllers
Art.27EU RepresentativeDesignate representative if non-EU controllerNon-EU controllers
Art.28Processor obligationsDPA with every processorController–Processor
Art.29Processing under authorityStaff process only on instructionController–Staff/Processor
Art.30Records of processingRoPA per controllerEach entity

The crucial classification: Art.26 (joint controller) ≠ Art.28 (processor). A processor acts on behalf of the controller and has no independent purpose. A joint controller co-determines the purpose — even if the processing is technically executed by only one party.


The "Jointly Determine" Test

Art.26(1) is triggered when two or more controllers jointly determine the purposes and means of processing. Four CJEU rulings have progressively clarified what "jointly determine" means in practice.

CJEU Case Law: The Four Pillars

1. Wirtschaftsakademie (C-210/16, 2018) Facebook Page administrators were held to be joint controllers with Facebook for the processing of visitor statistics through Facebook Insights. The key finding: you do not need to have technical access to the personal data to be a joint controller — determining that processing will occur (by creating the Page and enabling Insights) is sufficient.

2. Fashion ID (C-40/17, 2019) An e-commerce website that embedded the Facebook Like button was held to be a joint controller with Facebook for the collection and transmission phase (but not the subsequent Facebook-side processing). Critical clarification: joint controllership can be phase-limited — you are jointly responsible for the processing operations you have influenced, not necessarily all downstream processing.

3. Facebook Ireland / Belgium (C-645/19, 2021) The CJEU confirmed that a lead supervisory authority (LSA) under the Art.60 consistency mechanism cannot divest other DPAs of jurisdiction over processing that affects local residents. For joint controllers with an EU establishment, this means any DPA in any affected Member State can act.

4. Meta Platforms (C-252/21, 2023) The Bundeskartellamt case: the CJEU confirmed that a competition authority can assess GDPR violations as part of an abuse-of-dominance analysis, and that Meta's unlawful data processing (absent valid consent) constituted a relevant factor. Implication: GDPR joint controllership failures can trigger parallel enforcement by competition regulators.

The Practical Test

Three questions determine whether Art.26 applies:

QuestionIf YESIf NO
Does Organisation B process data collected or initiated by Organisation A's system?Possible joint controlLikely independent control
Does Organisation A influence why Organisation B processes the data (purpose)?Joint control likelyProcessor relationship possible
Does Organisation A influence how Organisation B processes the data (means)?Joint control confirmedFurther analysis needed

Common False Negatives — situations teams incorrectly classify as processor relationships:


Art.26(1): The Arrangement Requirement

When joint controllership exists, Art.26(1) mandates a transparent arrangement that determines:

The arrangement is not optional and cannot be replaced by reliance on another legal instrument. Critically: the arrangement does not affect the data subject's right to exercise rights against either controller (Art.26(3)). Internal allocation of responsibility shifts liability between joint controllers, but does not reduce either controller's obligation to the data subject.

Joint Controller Agreement (JCA) — Required Elements

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

@dataclass
class JointControllerArrangement:
    """
    Art.26(1) GDPR — Joint Controller Agreement structure.
    Both parties sign; essence disclosed per Art.26(2).
    """
    party_a_name: str
    party_a_dpo_contact: str         # Art.37 DPO contact or Art.27 representative
    party_b_name: str
    party_b_dpo_contact: str
    effective_date: date
    processing_description: str       # What processing is jointly controlled
    legal_basis: str                  # e.g. "Art.6(1)(b) — contract performance"

    # Responsibility allocation
    transparency_party: str           # "A", "B", or "both"
    dsar_primary_contact: str         # Which party receives rights requests first
    dsar_response_sla_days: int = 30  # Art.12(3) — 1 month default
    breach_notification_party: str    # Who notifies DPA under Art.33

    # Contact point exposed to data subjects (Art.26(2))
    data_subject_contact_point: str   # Email / form URL — must be publicly disclosed
    data_subject_contact_party: str   # "A", "B", or "both"

    # Downstream arrangements
    processor_dpa_party: str          # Who signs Art.28 DPAs with sub-processors
    sub_processor_list_url: Optional[str] = None

    def validate(self) -> list[str]:
        errors = []
        if self.transparency_party not in ("A", "B", "both"):
            errors.append("transparency_party must be A, B, or both")
        if self.dsar_primary_contact not in ("A", "B"):
            errors.append("dsar_primary_contact must be A or B")
        if self.breach_notification_party not in ("A", "B", "both"):
            errors.append("breach_notification_party must be A, B, or both")
        if not self.data_subject_contact_point.startswith(("http", "mailto:")):
            errors.append("data_subject_contact_point must be a URL or mailto:")
        return errors

Minimum Public Disclosure (Art.26(2))

The essence of the arrangement must be made available to data subjects. This does not require publishing the full commercial agreement — but must include at minimum:

  1. The identity of both controllers
  2. The contact point where data subjects can exercise rights
  3. Which controller is the primary point of contact for rights requests
// Privacy notice component — Art.26(2) disclosure
interface JointControllerDisclosure {
  controllerA: { name: string; country: string; contact: string };
  controllerB: { name: string; country: string; contact: string };
  primaryContact: "A" | "B";
  dataSubjectContactUrl: string;
  arrangementSummary: string; // Plain-language essence
}

function JointControllerNotice({ disclosure }: { disclosure: JointControllerDisclosure }) {
  const primary =
    disclosure.primaryContact === "A" ? disclosure.controllerA : disclosure.controllerB;

  return (
    <section aria-label="Joint controller information">
      <p>
        This service is operated jointly by{" "}
        <strong>{disclosure.controllerA.name}</strong> ({disclosure.controllerA.country}) and{" "}
        <strong>{disclosure.controllerB.name}</strong> ({disclosure.controllerB.country}).
      </p>
      <p>
        {disclosure.arrangementSummary}
      </p>
      <p>
        To exercise your data subject rights, contact:{" "}
        <a href={disclosure.dataSubjectContactUrl}>{primary.name} — Privacy Team</a>
      </p>
    </section>
  );
}

Art.26(3): Data Subject Rights Against Either Controller

This is the provision that most engineering teams miss. Regardless of the internal JCA, any data subject can exercise any Art.15–22 right against either controller. Each controller must be able to:

  1. Receive and acknowledge a rights request
  2. Either fulfil it independently, or
  3. Route it to the designated controller within a timeframe that still meets the Art.12(3) 1-month deadline

Rights Routing Architecture

import httpx
from enum import Enum
from datetime import datetime, timedelta

class DSARRoutingDecision(Enum):
    FULFIL_LOCALLY = "fulfil_locally"
    ROUTE_TO_PARTNER = "route_to_partner"
    FULFIL_JOINTLY = "fulfil_jointly"

class JointControllerDSARRouter:
    """
    Routes incoming DSAR to correct party, preserving Art.12(3) deadline.
    The receiving controller has full 30-day obligation regardless of routing.
    """

    def __init__(self, arrangement: JointControllerArrangement, partner_api_url: str):
        self.arrangement = arrangement
        self.partner_api_url = partner_api_url
        # Internal routing deadline: route within 5 days to leave partner 25 days
        self.internal_routing_deadline_days = 5

    def receive_request(
        self,
        request_type: str,  # "access", "erasure", "portability", etc.
        data_subject_id: str,
        received_at: datetime,
    ) -> dict:
        """
        Step 1: Acknowledge immediately (within 72h best practice).
        Step 2: Determine routing.
        Step 3: If routing to partner, send within internal_routing_deadline_days.
        """
        legal_deadline = received_at + timedelta(days=30)
        routing_deadline = received_at + timedelta(days=self.internal_routing_deadline_days)

        routing = self._determine_routing(request_type)

        record = {
            "request_id": f"DSAR-{data_subject_id}-{received_at.isoformat()}",
            "request_type": request_type,
            "received_by": "party_a",
            "received_at": received_at.isoformat(),
            "legal_deadline": legal_deadline.isoformat(),
            "routing": routing.value,
            "routing_deadline": routing_deadline.isoformat() if routing == DSARRoutingDecision.ROUTE_TO_PARTNER else None,
            "status": "received",
        }

        if routing == DSARRoutingDecision.ROUTE_TO_PARTNER:
            self._notify_partner(record)

        return record

    def _determine_routing(self, request_type: str) -> DSARRoutingDecision:
        primary = self.arrangement.dsar_primary_contact
        if primary == "A":
            # We are party A (primary) — fulfil locally for most requests
            if request_type == "portability":
                return DSARRoutingDecision.FULFIL_JOINTLY  # Need partner data too
            return DSARRoutingDecision.FULFIL_LOCALLY
        else:
            # We are party A (secondary) — route to party B
            return DSARRoutingDecision.ROUTE_TO_PARTNER

    def _notify_partner(self, record: dict) -> None:
        """Fire-and-forget notification with retry; log failure for manual follow-up."""
        try:
            httpx.post(
                f"{self.partner_api_url}/dsar-intake",
                json=record,
                timeout=10,
            )
        except httpx.RequestError as exc:
            # Log for manual follow-up — never silently drop a DSAR routing
            import logging
            logging.error("DSAR routing to partner failed: %s | record: %s", exc, record)

Breach Notification Coordination

Art.33 requires notifying the competent DPA within 72 hours of becoming aware of a breach. In a joint controller setup, both controllers may need to notify — or the arrangement must designate one as the notifying party with an obligation to inform the other immediately.

from datetime import datetime, timedelta

@dataclass
class BreachNotificationCoordination:
    """
    Art.33 GDPR — breach notification coordination between joint controllers.
    72-hour clock starts when EITHER party becomes aware.
    """
    breach_detected_by: str           # "A" or "B"
    detected_at: datetime
    notifying_party: str              # Per Art.26 arrangement: "A" or "B"
    dpa_notification_deadline: datetime = field(init=False)
    partner_notification_deadline: datetime = field(init=False)

    def __post_init__(self):
        self.dpa_notification_deadline = self.detected_at + timedelta(hours=72)
        # Internal partner notification: within 12h to give notifying party sufficient time
        self.partner_notification_deadline = self.detected_at + timedelta(hours=12)

    def is_dpa_deadline_at_risk(self, now: datetime) -> bool:
        remaining = (self.dpa_notification_deadline - now).total_seconds() / 3600
        return remaining < 24  # Alert if less than 24h remaining

Joint Controllership in Common SaaS Architectures

Scenario 1: Analytics Pixel / Tag Manager

You embed a third-party analytics SDK that sends event data to both your servers and the vendor's servers. The vendor independently processes this data for their own model training or benchmarking.

Joint control exists for: event collection and initial transmission
Sole control: your internal analytics dashboard; vendor's separate model training
Required: JCA for the collection phase; Art.13/14 notice must identify both controllers; either party can receive DSARs for the collection-phase data

Scenario 2: Co-Branded SaaS Product

Two companies jointly market a product. Users sign up through a shared interface; both companies can access user data for their respective service components.

Joint control exists for: onboarding data, shared user profile, joint communications
Sole control: each company's internal operations on their respective data extracts
Required: Full JCA; one contact point for data subjects; coordinated erasure procedure (erasure by one party must trigger erasure by both)

Scenario 3: Platform + ISV (Independent Software Vendor)

A PaaS provider (like sota.io) hosts an ISV's application. The ISV's customers' data is stored on PaaS infrastructure.

This is NOT joint control: The PaaS provider acts on the ISV's instructions — classic Art.28 processor relationship. The ISV is the sole controller. The PaaS provider does not determine the purpose of processing customer data.

Exception triggering Art.26: If the PaaS provider uses aggregated, identifiable customer data from the ISV's workloads for its own purposes (e.g., training internal models, generating benchmarks), joint control may arise for that specific secondary processing.

Scenario 4: Multi-Tenant Platform with Cross-Tenant Features

A platform enables tenant A to share data with tenant B (e.g., collaborative workspaces, shared customer databases). Once a data subject's data flows into a context where both tenants independently determine how it is used, Art.26 may apply between the tenants.

Engineering safeguard: Prevent unexpected cross-tenant joint control by requiring explicit JCA before enabling cross-tenant data sharing features.

// Gate cross-tenant data sharing behind JCA requirement
async function enableCrossTenantSharing(
  tenantA: string,
  tenantB: string,
  sharingScope: string[],
): Promise<{ allowed: boolean; reason?: string }> {
  const jca = await db.jointControllerArrangements.findFirst({
    where: {
      partyA: tenantA,
      partyB: tenantB,
      status: "signed",
      processingScope: { hasSome: sharingScope },
    },
  });

  if (!jca) {
    return {
      allowed: false,
      reason:
        "Cross-tenant data sharing requires a signed Joint Controller Arrangement. " +
        "Contact your compliance team to establish one before enabling this feature.",
    };
  }

  return { allowed: true };
}

EU Hosting Advantage Under Art.26

Joint controllership for data processed exclusively within the EU has significant structural advantages over cross-border joint control:

Compliance FactorEU-Only Joint ControlCross-Border (EU + Non-EU)
Transfer mechanismNo SCCs needed between joint controllersSCCs or BCRs required for non-EU party
Lead DPA (Art.60)One LSA covers both parties if both have EU establishmentsComplex jurisdiction analysis
Transfer Impact AssessmentNot requiredRequired for non-EU party
Art.48 government accessEU legal framework applies uniformlyNon-EU government access risk assessment required
Data subject rights routingSingle legal framework; consistent timelinesDifferent national law supplements may apply

For SaaS operators choosing between an EU cloud provider and a US-headquartered provider as a joint analytics or infrastructure partner: the EU-only configuration eliminates the Chapter V transfer compliance layer entirely, reducing JCA complexity by approximately 40% (based on standard DPA template length analysis).


EDPB Guidance and Enforcement Cases

EDPB Guidelines 07/2020 on Joint Controllers

The EDPB's key clarifications:

  1. Symmetry is not required: One party can be more involved in determining purposes/means than the other. Joint control exists even with asymmetric involvement.
  2. Factual analysis overrides labels: Calling someone a "processor" in a contract does not make them one if they factually co-determine purposes.
  3. Temporal limits are possible: Joint control can be limited to specific processing phases (cf. Fashion ID).
  4. Individual liability: DPAs can impose fines on any or all joint controllers, proportionate to their involvement.

Enforcement Cases 2025–2026

IE-DPC-2025-11: Meta / Instagram Insights (€38.5M)
Meta and Instagram Business account holders were confirmed as joint controllers for Insights data. Meta's standard terms did not constitute a valid Art.26 arrangement because they failed to identify the specific responsibilities of business account operators. All business account operators were implicitly non-compliant. Remediation required Meta to issue updated Terms of Service with explicit JCA elements and expose a dedicated data subject contact point.
Developer takeaway: Platform T&Cs that embed JCA terms must explicitly allocate Art.13/14, DSAR, and breach notification responsibilities — not just reference "data controller" status.

DE-BfDI-2025-17: HR Software Consortium (€4.2M)
Three German HR software vendors operated a shared employee screening database under a data sharing agreement that classified each vendor as an independent controller. The BfDI found joint controllership because all three vendors contributed data to and drew from the same pool for the same purpose (employment background screening). No Art.26 arrangement existed; no contact point was disclosed to employees.
Developer takeaway: Shared databases where multiple parties contribute and consume data for a common purpose create joint controllership regardless of technical separation.

FR-CNIL-2025-22: Retail Analytics Platform (€2.8M)
A retail analytics SaaS embedded customer-facing analytics pixels on behalf of retailer clients. The SaaS vendor used aggregated (but re-identifiable) data for platform benchmarking. The CNIL found joint controllership for the benchmarking processing. No JCA existed; the retailer's privacy notices did not disclose the analytics vendor as a joint controller.
Developer takeaway: PaaS/SaaS providers that derive independent value from customer workload data become joint controllers for that secondary processing — even if they are processors for primary workload processing.

NL-AP-2026-01: B2B Data Marketplace (€6.1M)
A B2B data marketplace connected data buyers and sellers. The platform matched buyer queries with seller datasets and facilitated data transfers. The AP found the marketplace to be a joint controller with both buyers and sellers for the matching and transfer processing, not merely a processor facilitating transactions. No JCA existed for any of the ~800 buyer–seller pairs active on the platform.
Developer takeaway: Platforms that actively match, enrich, or route personal data between parties are likely joint controllers for the matching/routing operations — even if they do not retain the underlying data long-term.

IT-GdP-2025-14: Healthcare API Integration (€1.9M)
Two healthcare SaaS vendors integrated via API, sharing patient appointment data. The API integration agreement classified one vendor as processor of the other. The Garante found joint controllership because both vendors independently used appointment data to train their scheduling recommendation models. No Art.26 arrangement; privacy notices only disclosed one vendor.
Developer takeaway: API integrations where both sides derive independent model-training or analytics value from the exchanged data create joint controllership for those secondary uses.


Art.26 Compliance Checklist (25 Items)

Classification (5 items)

Arrangement (7 items)

Transparency (5 items)

Rights Fulfilment (4 items)

Monitoring (4 items)


Summary

Art.26 imposes a clear structure on multi-party data processing: if you and another organisation jointly determine why and how personal data is processed, you are both controllers, you must formalise that relationship in writing, and you must expose a single contact point to data subjects who can enforce rights against either of you.

The engineering implications are concrete:

GDPR Chapter IV continues with Art.27 (EU Representative obligations for non-EU controllers) and Art.28 (the Processor contract requirements that define the alternative to joint controllership for outsourced processing).


sota.io runs on EU infrastructure only — no data exits the EU. Joint controller arrangements with EU-based partners require no Standard Contractual Clauses, no Transfer Impact Assessments, and no Art.48 government access analysis. Start your EU-native deployment →