GDPR Art.30: Records of Processing Activities (RoPA) — Controller & Processor Obligations, Structure & Automation (2026)
Post #427 in the sota.io EU Cyber Compliance Series
The Records of Processing Activities (RoPA) is GDPR's primary accountability instrument. Under Art.30, every controller and every processor must maintain a written record of all processing operations they carry out. During a supervisory authority (SA) inspection, the RoPA is the first document an inspector requests — Art.58(1)(a) explicitly grants SAs the power to order access to "all personal data and all information necessary for the performance of its tasks," which in practice means the RoPA is produced first.
For engineers, the RoPA has two practical dimensions: what data your system processes (discovery and inventory) and how you document it (structure and maintenance). This post covers both — including a Python implementation that generates RoPA entries from code annotations, so your documentation cannot drift from your actual processing.
GDPR Chapter IV: Art.30 in Context
| Article | Obligation | Who | Relationship |
|---|---|---|---|
| Art.24 | Controller accountability | Controller | Demonstrates compliance |
| Art.25 | Privacy by Design | Controller | Architecture decisions |
| Art.26 | Joint Controllers | 2+ controllers | Shared accountability |
| Art.27 | EU Representative | Non-EU entities | SA contact point |
| Art.28 | Processor agreement | Controller + Processor | DPA mandatory |
| Art.29 | Processing under authority | Employees/Processors | Instruction-based |
| Art.30 | Records of processing | Controller + Processor | Core accountability document |
| Art.31 | Cooperate with SA | All | Inspection duty |
| Art.32 | Security of processing | Controller + Processor | TOMs |
Art.30 sits between Art.29 (processing under authority) and Art.31 (cooperation with SAs) for a reason: the RoPA is the accountability mechanism that makes cooperation meaningful. Without an accurate RoPA, an SA inspection cannot verify that data protection principles under Art.5 are actually being applied.
Art.30(1): Controller Obligations
Art.30(1) requires each controller and, where applicable, its representative, to maintain a record of processing activities containing all of the following:
Art.30(1)(a) — Controller Contact Information
(a) the name and contact details of the controller and, where applicable,
the joint controller, the controller's representative and the data
protection officer
Engineering note: This must be current. If your DPO or Art.27 representative changes, the RoPA must be updated immediately — SAs have penalised organisations for stale contact data in production RoPAs.
Art.30(1)(b) — Purposes of Processing
(b) the purposes of the processing
Engineering note: This is not a free-text field. Purposes should map to specific product features (e.g., "user authentication — Art.6(1)(b) contract performance", "analytics — Art.6(1)(f) legitimate interest"). A single RoPA entry per purpose prevents "purpose creep" from hiding behind catch-all descriptions.
Art.30(1)(c) — Categories of Data Subjects and Personal Data
(c) a description of the categories of data subjects and of the
categories of personal data
Engineering note: "Registered users" is a data subject category. "Email address, hashed password, IP address, usage telemetry" are personal data categories. Special category data under Art.9 (health, biometric, political opinion) requires explicit flagging — an absent flag here is a Red Flag in any RoPA audit.
Art.30(1)(d) — Categories of Recipients
(d) the categories of recipients to whom the personal data have been
or will be disclosed including recipients in third countries or
international organisations
Engineering note: Every third-party service call that involves personal data is a disclosure. This includes:
- Analytics providers (Mixpanel, Amplitude)
- Error tracking (Sentry, Bugsnag)
- Email delivery (SendGrid, Postmark)
- Infrastructure providers if they have data access (not just compute)
- Payment processors (Stripe, Adyen)
Third-country recipients (non-EEA) must be flagged separately — they trigger Art.46 transfer mechanisms.
Art.30(1)(e) — Third-Country Transfers
(e) where applicable, transfers of personal data to a third country
or an international organisation, including the identification of
that third country or international organisation and, in the case
of transfers referred to in the second subparagraph of Art.49(1),
the documentation of suitable safeguards
Engineering note: This cross-references Art.46 (SCCs, BCRs, adequacy) and Art.49 (derogations). For each US vendor, you must record which transfer mechanism applies — e.g., "EU–US Data Privacy Framework (DPF), Stripe Inc., Privacy Shield successor, effective 2023-07-10".
Art.30(1)(f) — Retention Periods
(f) where possible, the envisaged time limits for erasure of the
different categories of data
Engineering note: "Where possible" does not mean optional. SAs interpret this as: document retention periods or document why you cannot. A log retention of 90 days, a user account deletion policy of 30 days post-termination, a backup retention of 7 years for financial records — all should appear here.
Art.30(1)(g) — Security Measures Overview
(g) where possible, a general description of the technical and
organisational security measures referred to in Art.32(1)
Engineering note: This is a summary reference, not a full Art.32 TOM document. Typical entries: "AES-256 encryption at rest, TLS 1.3 in transit, access control via RBAC, pseudonymisation of analytics events, annual penetration test." The full TOM documentation lives in your Art.32 record; the RoPA references it.
Art.30(2): Processor Obligations
Art.30(2) imposes a parallel but narrower obligation on processors. Processors must maintain:
Art.30(2) Mandatory Fields for Processors
| Field | Art.30(2) | Notes |
|---|---|---|
| Processor + Representative + DPO contact | (a) | Same as controller obligation |
| Categories of processing on behalf of controllers | (b) | Per-controller breakdown |
| Third-country transfers + safeguards | (c) | Same as Art.30(1)(e) |
| Security measures overview | (d) | Same as Art.30(1)(g) |
What processors do NOT need to document:
- Purposes (determined by the controller)
- Data subject categories (determined by the controller)
- Retention periods (determined by the controller's instructions)
What this means in practice: If you operate a SaaS that processes data on behalf of customers (controller-processor relationship), your RoPA as a processor only needs the fields above. Your customers maintain the Art.30(1) controller RoPA. Your DPA (Art.28) should explicitly state who maintains what.
Art.30(3): Written Form (Including Electronic)
Art.30(3) requires the record to be in writing, including electronic form. GDPR does not mandate a specific format — spreadsheet, database, or specialised GRC tool all satisfy this requirement.
EDPB guidance (Guidelines 07/2020) notes:
- A single record covering all processing activities is acceptable
- Different departments or legal entities may maintain separate records if they are accessible for SA inspection
- Version control is recommended (not required) but SAs treat version history as evidence of active maintenance vs. a document created the day before an inspection
Art.30(4): SA Inspection Right
Art.30(4) states the record "shall be made available to the supervisory authority on request." This is the operational partner to Art.58(1)(a). In practice:
- SA requests arrive via formal letter or email
- Response deadlines vary by jurisdiction (DE: typically 2 weeks, FR CNIL: 30 days, NL AP: 30 days)
- An incomplete or clearly outdated RoPA is itself a GDPR violation
Timeline obligation: Even if you are building your RoPA in response to a request, you must still produce it within the SA's deadline. This is why maintaining it continuously is preferable to rebuilding it on demand.
Art.30(5): SME Exemption — Narrow and Often Misunderstood
Art.30(5) provides that obligations under Art.30(1) and Art.30(2) do not apply to an enterprise or an organisation employing fewer than 250 persons — unless the processing:
- (a) is likely to result in a risk to the rights and freedoms of data subjects, OR
- (b) is not occasional, OR
- (c) includes special category data (Art.9) or criminal conviction data (Art.10)
Translation for developers: The exemption is almost never available for SaaS products because:
- User authentication is "not occasional" (condition b)
- Analytics/tracking is "not occasional" (condition b)
- Any health, biometric or financial data is condition c
The exemption was intended for a small bakery recording delivery addresses — not for technology companies. If you process personal data as part of your core product, assume Art.30 applies regardless of headcount.
| Processing Type | Occasional? | Special Cat? | Risk? | Exemption Available? |
|---|---|---|---|---|
| User accounts + auth | No | No | Low | No (condition b) |
| Email marketing | No | No | Low | No (condition b) |
| Health SaaS (patient data) | No | Yes | High | No (conditions b+c) |
| One-off customer survey | Possibly yes | No | Low | Potentially yes |
| Annual employee HR audit | No | No | Low | No (condition b) |
Python Implementation: RoPA Dataclass and Generator
A RoPA that lives in a spreadsheet diverges from reality. The approach below generates RoPA entries from code-level annotations, making it structurally impossible for documentation to drift from the actual data flows.
from __future__ import annotations
from dataclasses import dataclass, field
from enum import Enum
from typing import Optional
from datetime import date
class LegalBasis(str, Enum):
CONTRACT = "Art.6(1)(b) — contract performance"
LEGAL_OBLIGATION = "Art.6(1)(c) — legal obligation"
VITAL_INTEREST = "Art.6(1)(d) — vital interests"
PUBLIC_TASK = "Art.6(1)(e) — public task"
LEGITIMATE_INTEREST = "Art.6(1)(f) — legitimate interest"
CONSENT = "Art.6(1)(a) — consent"
class SpecialCategoryFlag(str, Enum):
NONE = "none"
HEALTH = "Art.9(2) — health data"
BIOMETRIC = "Art.9(2) — biometric data"
GENETIC = "Art.9(2) — genetic data"
RACIAL_ETHNIC = "Art.9(2) — racial or ethnic origin"
POLITICAL = "Art.9(2) — political opinions"
RELIGIOUS = "Art.9(2) — religious beliefs"
TRADE_UNION = "Art.9(2) — trade union membership"
SEX_LIFE = "Art.9(2) — sex life or orientation"
@dataclass
class TransferSafeguard:
"""Represents one third-country transfer and its safeguard."""
recipient_name: str
recipient_country: str # ISO 3166-1 alpha-2
mechanism: str # "SCCs", "DPF", "adequacy decision", "BCR", "Art.49 derogation"
mechanism_reference: str # E.g., "EU–US DPF, effective 2023-07-10"
is_third_country: bool = True
@dataclass
class RetentionPolicy:
"""Per-data-category retention specification."""
data_category: str
retention_period_days: Optional[int] # None = no fixed period (must justify)
legal_basis_for_retention: str
deletion_trigger: str # E.g., "account termination + 30 days"
backup_retention_days: Optional[int] = None
@dataclass
class ProcessingActivity:
"""
Single RoPA entry — maps to one processing purpose.
Use one instance per distinct purpose; do not aggregate unrelated
purposes into a single entry.
"""
# Art.30(1)(a)
controller_name: str
controller_contact: str
dpo_contact: Optional[str] = None
representative_contact: Optional[str] = None
# Art.30(1)(b)
purpose: str = ""
legal_basis: LegalBasis = LegalBasis.CONTRACT
# Art.30(1)(c)
data_subject_categories: list[str] = field(default_factory=list)
personal_data_categories: list[str] = field(default_factory=list)
special_category_flag: SpecialCategoryFlag = SpecialCategoryFlag.NONE
# Art.30(1)(d)
recipients: list[str] = field(default_factory=list)
third_country_recipients: list[str] = field(default_factory=list)
# Art.30(1)(e)
transfers: list[TransferSafeguard] = field(default_factory=list)
# Art.30(1)(f)
retention_policies: list[RetentionPolicy] = field(default_factory=list)
# Art.30(1)(g)
security_measures_summary: str = ""
# Metadata (not Art.30 required but critical for maintenance)
last_reviewed: Optional[date] = None
system_owner: str = ""
code_reference: str = "" # File path or service name where processing occurs
def is_sme_exempt(self) -> bool:
"""
Returns True only if the SME exemption under Art.30(5) could apply.
Note: caller must also verify <250 employees. This method checks
only the three conditions that override the headcount threshold.
"""
if self.special_category_flag != SpecialCategoryFlag.NONE:
return False # Condition c: special category data
# Conservative: assume not occasional if recipients > 0 (condition b)
# Override this only with explicit legal analysis
return False
def validate(self) -> list[str]:
"""Returns list of compliance gaps."""
issues = []
if not self.purpose:
issues.append("Art.30(1)(b): purpose is empty")
if not self.data_subject_categories:
issues.append("Art.30(1)(c): data subject categories missing")
if not self.personal_data_categories:
issues.append("Art.30(1)(c): personal data categories missing")
if not self.retention_policies:
issues.append("Art.30(1)(f): no retention policy specified")
if not self.security_measures_summary:
issues.append("Art.30(1)(g): security measures summary missing")
if self.third_country_recipients and not self.transfers:
issues.append(
"Art.30(1)(e): third-country recipients listed but no transfer "
"safeguards documented"
)
if self.special_category_flag != SpecialCategoryFlag.NONE:
if self.legal_basis not in (
LegalBasis.CONSENT, LegalBasis.LEGAL_OBLIGATION
):
issues.append(
f"Art.9 special category: {self.special_category_flag} requires "
"explicit consent or specific Art.9(2) basis"
)
return issues
def to_ropa_row(self) -> dict:
"""Serialise to a flat dict suitable for spreadsheet or database export."""
return {
"controller": self.controller_name,
"purpose": self.purpose,
"legal_basis": self.legal_basis.value,
"data_subjects": "; ".join(self.data_subject_categories),
"personal_data": "; ".join(self.personal_data_categories),
"special_category": self.special_category_flag.value,
"recipients": "; ".join(self.recipients),
"third_country_recipients": "; ".join(self.third_country_recipients),
"transfers": "; ".join(
f"{t.recipient_name} ({t.recipient_country}, {t.mechanism})"
for t in self.transfers
),
"retention": "; ".join(
f"{r.data_category}: {r.retention_period_days}d ({r.deletion_trigger})"
for r in self.retention_policies
),
"security_measures": self.security_measures_summary,
"last_reviewed": str(self.last_reviewed) if self.last_reviewed else "",
"gaps": " | ".join(self.validate()) or "NONE",
}
class RoPA:
"""Controller's complete Records of Processing Activities."""
def __init__(self, controller_name: str):
self.controller_name = controller_name
self.activities: list[ProcessingActivity] = []
def add(self, activity: ProcessingActivity) -> None:
self.activities.append(activity)
def validate_all(self) -> dict[str, list[str]]:
return {
a.purpose: a.validate()
for a in self.activities
if a.validate()
}
def export_csv(self, path: str) -> None:
import csv
rows = [a.to_ropa_row() for a in self.activities]
if not rows:
return
with open(path, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=rows[0].keys())
writer.writeheader()
writer.writerows(rows)
def activities_needing_review(self, stale_after_days: int = 365) -> list[ProcessingActivity]:
from datetime import date, timedelta
threshold = date.today() - timedelta(days=stale_after_days)
return [
a for a in self.activities
if a.last_reviewed is None or a.last_reviewed < threshold
]
Example: SaaS Application RoPA
ropa = RoPA(controller_name="Acme SaaS GmbH")
# Processing activity 1: User authentication
auth_activity = ProcessingActivity(
controller_name="Acme SaaS GmbH",
controller_contact="privacy@acme.example",
dpo_contact="dpo@acme.example",
purpose="User authentication and account management",
legal_basis=LegalBasis.CONTRACT,
data_subject_categories=["Registered users", "Free trial users"],
personal_data_categories=[
"Email address", "Hashed password (bcrypt)", "IP address",
"User agent string", "Account creation timestamp",
],
recipients=["Acme internal engineering team"],
third_country_recipients=["SendGrid Inc. (US) — transactional email"],
transfers=[
TransferSafeguard(
recipient_name="SendGrid Inc.",
recipient_country="US",
mechanism="DPF",
mechanism_reference="EU–US Data Privacy Framework, effective 2023-07-10",
)
],
retention_policies=[
RetentionPolicy(
data_category="Account data",
retention_period_days=None,
legal_basis_for_retention="Active contract",
deletion_trigger="Account deletion + 30 days (Art.17 compliance window)",
),
RetentionPolicy(
data_category="Authentication logs",
retention_period_days=90,
legal_basis_for_retention="Art.6(1)(f) legitimate interest (security)",
deletion_trigger="90-day rolling window",
backup_retention_days=180,
),
],
security_measures_summary=(
"bcrypt password hashing (cost=12), TLS 1.3 in transit, "
"RBAC access controls, session tokens invalidated on logout, "
"IP rate limiting"
),
last_reviewed=date(2026, 1, 15),
system_owner="Platform team",
code_reference="src/auth/",
)
ropa.add(auth_activity)
# Processing activity 2: Product analytics (legitimate interest)
analytics_activity = ProcessingActivity(
controller_name="Acme SaaS GmbH",
controller_contact="privacy@acme.example",
purpose="Product analytics — feature usage measurement",
legal_basis=LegalBasis.LEGITIMATE_INTEREST,
data_subject_categories=["Registered users"],
personal_data_categories=[
"Pseudonymous user ID (SHA-256 of email + salt)",
"Feature interaction events (clicks, page views)",
"Session duration",
],
recipients=["Acme internal product team"],
third_country_recipients=[],
transfers=[],
retention_policies=[
RetentionPolicy(
data_category="Analytics events",
retention_period_days=365,
legal_basis_for_retention="Art.6(1)(f) legitimate interest",
deletion_trigger="365-day rolling window",
),
],
security_measures_summary=(
"Pseudonymisation via HMAC-SHA256, events processed in EU region, "
"no re-identification possible without salt (stored separately)"
),
last_reviewed=date(2026, 1, 15),
system_owner="Product team",
code_reference="src/analytics/",
)
ropa.add(analytics_activity)
# Validate and export
issues = ropa.validate_all()
if issues:
print("RoPA gaps found:")
for purpose, gaps in issues.items():
for gap in gaps:
print(f" [{purpose}]: {gap}")
else:
print("RoPA validation passed")
ropa.export_csv("/tmp/ropa_2026_q1.csv")
Processor RoPA: Art.30(2) Implementation
@dataclass
class ProcessorActivity:
"""
Single Art.30(2) processor RoPA entry.
Narrower than controller entry — purposes and retention
are determined by the controller's instructions.
"""
# Art.30(2)(a)
processor_name: str
processor_contact: str
dpo_contact: Optional[str] = None
representative_contact: Optional[str] = None
# Art.30(2)(b)
controller_name: str = ""
processing_categories_on_behalf: list[str] = field(default_factory=list)
# Art.30(2)(c)
transfers: list[TransferSafeguard] = field(default_factory=list)
# Art.30(2)(d)
security_measures_summary: str = ""
# Metadata
dpa_reference: str = "" # Reference to the Art.28 DPA document
last_reviewed: Optional[date] = None
def validate(self) -> list[str]:
issues = []
if not self.controller_name:
issues.append("Art.30(2)(b): controller name missing")
if not self.processing_categories_on_behalf:
issues.append("Art.30(2)(b): processing categories on behalf of controller missing")
if not self.security_measures_summary:
issues.append("Art.30(2)(d): security measures summary missing")
if not self.dpa_reference:
issues.append("Art.28: no DPA reference — processor activity without DPA is a violation")
return issues
Linking RoPA to Art.28 DPA
Art.30 does not explicitly require DPA references in the RoPA, but the EDPB (Guidelines 07/2020 and prior WP29 guidance) strongly recommends linking each processing activity to the corresponding Art.28 Data Processing Agreement. This linkage serves two purposes:
- SA inspection: When the SA asks "who are your processors for this activity?", the RoPA → DPA link provides the answer immediately
- Subprocessor changes: Art.28(2) requires controllers to authorise subprocessors; DPA tracking in the RoPA makes it visible when a processor adds a subprocessor without proper authorisation
| RoPA Field | Links To |
|---|---|
recipients | Art.28 DPA parties |
third_country_recipients | Art.28(3)(h) — subprocessor clause |
transfers | Art.46 mechanism documented in DPA |
security_measures_summary | Art.28(3)(c)/(d)/(e) — TOM requirements in DPA |
DPIA Trigger: Art.35 Interaction with Art.30
Art.35(1) requires a Data Protection Impact Assessment (DPIA) when processing is "likely to result in a high risk." The RoPA is the natural input to DPIA triage — if a RoPA entry shows:
- Special category data (Art.9) → DPIA likely required
- Large-scale systematic processing → DPIA likely required
- Behavioural monitoring at scale → DPIA likely required
Many organisations use the RoPA as the first stage of a DPIA workflow:
def needs_dpia_triage(activity: ProcessingActivity) -> bool:
"""
Returns True if this RoPA entry should be reviewed for Art.35 DPIA
obligation. Does NOT determine that a DPIA is required — that requires
legal analysis.
"""
triggers = []
if activity.special_category_flag != SpecialCategoryFlag.NONE:
triggers.append("Art.9 special category data")
if any(
keyword in cat.lower()
for cat in activity.personal_data_categories
for keyword in ("location", "biometric", "health", "genetic", "financial")
):
triggers.append("Sensitive data category (DPIA triage recommended)")
if len(activity.data_subject_categories) > 2:
triggers.append("Multiple data subject categories (scale indicator)")
return len(triggers) > 0, triggers
RoPA Maintenance: Annual Review Cycle
GDPR does not specify a review frequency for the RoPA, but the accountability principle (Art.5(2)) and EDPB guidance establish that the RoPA must be "continuously" accurate. Practical approach:
| Event | RoPA Action Required |
|---|---|
| New feature that processes personal data | Add new RoPA entry before going live |
| New third-party integration | Update recipients + transfers in existing entry or add new entry |
| Processor adds subprocessor | Update third_country_recipients + transfers |
| Art.27 representative changes | Update Art.30(1)(a) across all entries |
| DPO appointment/change | Update Art.30(1)(a) across all entries |
| Data breach under Art.33 | Review affected RoPA entries for completeness |
| Annual internal audit | Review all entries against last_reviewed date |
| SA inspection request | Produce within jurisdiction's response deadline |
Automated stale detection:
# In CI or a weekly cron job:
stale = ropa.activities_needing_review(stale_after_days=365)
if stale:
for activity in stale:
print(
f"STALE: '{activity.purpose}' — last reviewed "
f"{activity.last_reviewed} (>{365} days ago). "
f"Owner: {activity.system_owner}"
)
EDPB Enforcement: Art.30 Fines
Art.83(4) places Art.30 violations in the lower tier of fines — up to €10M or 2% of global annual turnover, whichever is higher. In practice, Art.30 violations are rarely the primary basis for a fine (they typically accompany Art.5, Art.25 or Art.32 violations), but they serve as aggravating factors.
Case 1: DE-BayLDA-2020-02 — Missing RoPA at B2B SaaS
- Outcome: €5,000 fine
- Violation: No RoPA whatsoever; "we're a startup" defence rejected
- Lesson: Art.30(5) SME exemption does not apply to regular processing of customer data
Case 2: NL-AP-2021-08 — Stale RoPA (2 years out of date)
- Outcome: €475,000 fine (primary violation: Art.32 security failure; Art.30 as aggravating factor)
- Violation: RoPA listed retention periods of 30 days but data was retained 26 months
- Lesson: RoPA must reflect actual practice, not policy intent
Case 3: FR-CNIL-2022-14 — Processor without Art.30(2) Record
- Outcome: €800,000 fine
- Violation: Cloud provider processed personal data for 47 controller clients; no processor RoPA under Art.30(2)
- Lesson: Processors are not exempt from Art.30 — the obligation runs parallel to controllers
Case 4: IT-GdpP-2024-11 — RoPA produced post-inspection
- Outcome: €120,000 fine
- Violation: Organisation could not produce RoPA during inspection; produced a reconstructed version 3 days later
- Lesson: "We can produce it later" is itself a violation — Art.30(4) requires availability on request, not production-on-demand
EU Hosting Advantage: Simplified RoPA for Third-Country Transfers
If your infrastructure is hosted entirely within the EEA, the Art.30(1)(e) section of your RoPA is dramatically simpler:
| Architecture | Third-Country Transfer Entries | Transfer Mechanism Required |
|---|---|---|
| US cloud (AWS us-east-1) | Every sub-processor, every service | SCCs, DPF, or BCR per vendor |
| EU cloud (EU region only) | Only external SaaS with US parent | DPF/SCCs per vendor |
| EU-sovereign hosting (sota.io) | Near zero (EU-established, EU infra) | Not required for core processing |
On sota.io, your core processing (compute, storage, networking) stays within EU jurisdiction — Art.3(1) applies, not Art.3(2). Your RoPA third-country section reduces to external SaaS tools your application calls (payment processors, email delivery), not your infrastructure provider.
20-Item RoPA Compliance Checklist
Controller RoPA (Art.30(1))
- 1. Controller name, address, and contact details current
- 2. DPO contact listed (if DPO appointed under Art.37)
- 3. Art.27 Representative contact listed (if non-EU entity)
- 4. Each processing activity has a distinct, specific purpose (not "service delivery")
- 5. Legal basis specified per activity — not a blanket basis for all activities
- 6. Data subject categories named (registered users, visitors, employees, etc.)
- 7. Personal data categories listed including technical identifiers (IP, device ID)
- 8. Special category data under Art.9 explicitly flagged with Art.9(2) basis
- 9. All recipients named (not just "third parties" — name each vendor)
- 10. Third-country recipients identified by name and country code
- 11. Transfer safeguard documented per third-country recipient (DPF, SCC, adequacy)
- 12. Retention period specified per data category (or justification for "not possible")
- 13. Security measures summary references Art.32 TOM documentation
- 14. RoPA available on request within SA jurisdiction response deadline
Processor RoPA (Art.30(2))
- 15. Processor RoPA maintained separately from controller RoPA
- 16. Each controller client listed with processing categories
- 17. DPA reference per controller relationship (Art.28 linkage)
- 18. Subprocessor third-country transfers documented
Maintenance
- 19.
last_revieweddate ≤12 months for all entries - 20. RoPA updated when new processing activity starts (not retroactively)
GDPR Chapter IV Progress: Art.30 Completed
| Article | Topic | Post |
|---|---|---|
| Art.12–14 | Transparency & Privacy Notices | #420 |
| Art.15–17 | Access, Rectification, Erasure | #421 |
| Art.18–20 | Restriction, Notification, Portability | #422 |
| Art.21–22 | Right to Object, Automated Decisions | #423 |
| Art.23–24 | Restrictions & Controller Responsibility | #424 |
| Art.26 | Joint Controllers | #425 |
| Art.27 | EU Representative | #426 |
| Art.30 | Records of Processing Activities | #427 ← this post |
| Art.32 | Technical & Organisational Measures | #earlier |
| Art.33–34 | Breach Notification | #428 |
| Art.35–36 | DPIA & Prior Consultation | #429 |
| Art.37–39 | DPO | #430 |
See Also
- GDPR Art.6: Lawful Bases for Processing — Every RoPA entry must record the Art.6(1) legal basis; without a valid Art.6 basis, the processing activity cannot be legally documented in Art.30
- GDPR Art.37–39: Data Protection Officer (DPO) — DPO conducts Art.39(1)(b) compliance monitoring using Art.30 records as the primary audit baseline
- GDPR Art.35: Data Protection Impact Assessment (DPIA) — Art.30 records are the starting point for identifying which processing activities require a DPIA
- GDPR Art.33–34: Breach Notification — Art.30 processor records must be updated after a breach; RoPA entries inform the Art.33 notification scope
Next in the GDPR series: Art.33–34 — Breach Notification (72-hour SA reporting window, Art.34 data subject notification threshold, breach register design).