Privacy Notices API
Manage privacy notices, processing purposes, and Records of Processing Activities (RoPA) under /api/v1.0.
Privacy notices are space-scoped, versioned policy records. Each notice owns one or more processing purposes, and catalog products reference those purposes from their legal and compliance metadata. RoPA endpoints combine privacy notice mappings with product metadata, sensitivity, owners, recipients, and lawful basis values so privacy teams can review coverage and export evidence.
Endpoints Overview
| Method | Endpoint | Description |
|---|---|---|
| GET | /spaces/{slug}/ropa-report | Generate a paginated RoPA report for a space |
| GET | /spaces/{slug}/ropa-summary | Return RoPA summary counts for a space |
| GET | /spaces/{slug}/ropa-export | Export the full space RoPA report as CSV |
| GET | /spaces/{slug}/privacy-notices | List privacy notices in a space |
| POST | /spaces/{slug}/privacy-notices | Create a privacy notice |
| GET | /spaces/{slug}/privacy-notices/{notice_id} | Get a privacy notice |
| PUT | /spaces/{slug}/privacy-notices/{notice_id} | Update a privacy notice |
| DELETE | /spaces/{slug}/privacy-notices/{notice_id} | Delete a privacy notice |
| POST | /spaces/{slug}/privacy-notices/{notice_id}/purposes | Add a processing purpose |
| PUT | /spaces/{slug}/privacy-notices/{notice_id}/purposes/{purpose_id} | Update a processing purpose |
| DELETE | /spaces/{slug}/privacy-notices/{notice_id}/purposes/{purpose_id} | Delete a processing purpose |
| GET | /spaces/org/{org_slug}/ropa-report | Generate an organization-wide RoPA report |
| GET | /spaces/org/{org_slug}/ropa-summary | Return organization-wide RoPA summary counts |
| GET | /spaces/org/{org_slug}/privacy-notices | List notices aggregated across organization spaces |
| GET | /spaces/org/{org_slug}/ropa-export | Export organization-wide RoPA as CSV |
Data Model
Privacy Notice
{
"id": "62e9a747-6ebb-4b81-991b-4f9caa13a1de",
"space_id": "cc7a4f41-3923-41c0-9fd2-f1e030f5bd62",
"space_name": "Marketing Analytics",
"name": "Global Privacy Policy",
"version": "v2.1",
"status": "ACTIVE",
"content_url": "https://example.com/privacy",
"valid_from": "2026-03-01T00:00:00Z",
"valid_to": null,
"created_at": "2026-02-10T09:30:00Z",
"updated_at": "2026-03-01T11:15:00Z",
"purposes": [
{
"id": "e8027c92-9afb-49a9-bbd7-caf9e3d2946c",
"notice_id": "62e9a747-6ebb-4b81-991b-4f9caa13a1de",
"name": "Marketing Analytics",
"description": "Analyze customer behavior for campaign measurement.",
"is_essential": false,
"created_at": "2026-02-10T09:35:00Z",
"updated_at": "2026-02-10T09:35:00Z"
}
]
}
| Field | Type | Notes |
|---|---|---|
name | string | Human-readable notice name. |
version | string | Version label such as v1.0 or 2026.03. |
status | string | DRAFT, ACTIVE, or ARCHIVED. The API stores the supplied string; clients should use the shipped status values. |
content_url | string or null | Hosted policy URL. Create and update requests validate this as an external HTTP(S) URL when provided. |
valid_from | datetime or null | Effective date for this version. |
valid_to | datetime or null | Superseded or retired date. |
purposes | array | Processing purposes owned by this notice. |
space_name | string or null | Populated for organization aggregation responses. |
Processing Purpose
| Field | Type | Notes |
|---|---|---|
id | UUID | Purpose identifier used by product compliance metadata. |
notice_id | UUID | Owning privacy notice. |
name | string | Required purpose name. |
description | string or null | Optional description for legal or product review. |
is_essential | boolean | Whether the processing is strictly necessary for the core service. Defaults to false. |
Deleting a privacy notice cascades to its processing purposes.
Space RoPA Report
Generate a paginated, filterable Record of Processing Activities report for a space.
GET /api/v1.0/spaces/{slug}/ropa-report
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 20 | Page size from 1 to 100. |
offset | integer | 0 | Records to skip. |
search | string | - | Search product name, lawful basis, or processing purpose. |
lawful_basis | string | - | Filter by lawful basis value. |
environment | string | - | Filter by product environment. |
sensitivity | string | - | Filter by product sensitivity level. |
mapping_status | string | all | mapped, unmapped, or all. |
Response
[
{
"product_id": "0a21b71f-6b73-4a58-a1e8-7f9dd2a4f61d",
"product_name": "Customer Events",
"product_type": "table",
"product_kind": "dataset",
"product_source_id": "de0d2d90-d459-4477-bc2a-035ec3fb21b5",
"path": "warehouse.analytics.customer_events",
"environment": "production",
"space_slug": null,
"space_name": null,
"lawful_basis": "Legitimate Interest",
"retention_period": "36 months",
"data_subject_categories": ["Customers", "Prospects"],
"processing_purpose_id": "e8027c92-9afb-49a9-bbd7-caf9e3d2946c",
"processing_purpose_name": "Marketing Analytics",
"privacy_notice_id": "62e9a747-6ebb-4b81-991b-4f9caa13a1de",
"privacy_notice_name": "Global Privacy Policy",
"sensitivity": "PII",
"data_categories": ["PII"],
"recipients": ["Campaign Reporting"],
"owner_id": "a91ed407-ded8-4231-a3f5-0501a83c71a2",
"owner_name": "Alex Kim",
"has_compliance_gap": false
}
]
Pagination headers include:
| Header | Meaning |
|---|---|
X-Total-Count | Total number of matching records. |
X-Page | Current 1-indexed page number. |
X-Has-More | true when more results exist after the current page. |
has_compliance_gap is true when product sensitivity indicates PII or PHI and the product lacks a lawful basis.
RoPA Summary
Return aggregate counts used by the RoPA dashboard.
GET /api/v1.0/spaces/{slug}/ropa-summary
Response
{
"total": 124,
"mapped": 102,
"unmapped": 22,
"missing_basis": 8,
"with_compliance_gap": 5
}
RoPA Export
Export the full space RoPA report as CSV.
GET /api/v1.0/spaces/{slug}/ropa-export
The response is text/csv with a Content-Disposition filename of ropa-report-{slug}.csv.
CSV columns:
product_name,space_slug,environment,sensitivity,lawful_basis,retention_period,data_subject_categories,processing_purpose_name,privacy_notice_name,data_categories,recipients,owner_name,has_compliance_gap
Exports include up to 10,000 records. Filters are not accepted on the export endpoint in the current API.
List Privacy Notices
GET /api/v1.0/spaces/{slug}/privacy-notices
Response
[
{
"id": "...",
"space_id": "...",
"name": "Global Privacy Policy",
"version": "v1.2",
"status": "ACTIVE",
"content_url": "https://example.com/privacy-policy",
"valid_from": "2026-01-01T00:00:00Z",
"valid_to": null,
"created_at": "2025-12-01T10:00:00Z",
"updated_at": "2026-01-15T14:30:00Z",
"purposes": [
{
"id": "...",
"notice_id": "...",
"name": "Marketing Analytics",
"description": "Analyze customer behavior for marketing campaigns",
"is_essential": false,
"created_at": "2025-12-01T10:00:00Z",
"updated_at": "2025-12-01T10:00:00Z"
}
]
}
]
Get Privacy Notice
GET /api/v1.0/spaces/{slug}/privacy-notices/{notice_id}
Returns a single privacy notice with its purposes. Returns 404 when the notice does not belong to the space or does not exist.
Create Privacy Notice
POST /api/v1.0/spaces/{slug}/privacy-notices
Request Body
{
"name": "Global Privacy Policy",
"version": "v1.0",
"status": "DRAFT",
"content_url": "https://example.com/privacy-policy",
"valid_from": "2026-03-01T00:00:00Z"
}
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Name of the privacy notice |
version | string | Yes | Version string (e.g., v1.0) |
status | string | No | DRAFT (default), ACTIVE, or ARCHIVED |
content_url | string | No | Link to the hosted policy text |
valid_from | datetime | No | When this version comes into effect |
valid_to | datetime | No | When this version was superseded |
Response: Full privacy notice object with purposes.
Update Privacy Notice
PUT /api/v1.0/spaces/{slug}/privacy-notices/{notice_id}
Accepts the same fields as creation. Only provided fields are updated (partial update).
Request Body
{
"status": "ACTIVE",
"valid_from": "2026-03-01T00:00:00Z"
}
Returns 404 when the notice does not belong to the space or does not exist.
Delete Privacy Notice
DELETE /api/v1.0/spaces/{slug}/privacy-notices/{notice_id}
Deletes the notice and its processing purposes.
Response: {"message": "Privacy notice deleted"}
Add Processing Purpose
POST /api/v1.0/spaces/{slug}/privacy-notices/{notice_id}/purposes
Request Body
{
"name": "Marketing Analytics",
"description": "Analyze customer behavior for marketing campaigns",
"is_essential": false
}
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Name of the processing purpose |
description | string | No | Detailed description |
is_essential | boolean | No | Whether this processing is strictly necessary (default: false) |
Response: Processing purpose object.
Returns 404 when the notice does not belong to the space or does not exist.
Update Processing Purpose
PUT /api/v1.0/spaces/{slug}/privacy-notices/{notice_id}/purposes/{purpose_id}
Accepts name, description, and is_essential. Only provided fields are updated.
Request Body
{
"description": "Analyze customer behavior for campaign measurement and audience reporting.",
"is_essential": false
}
Returns 404 when the purpose cannot be found in the space.
Delete Processing Purpose
DELETE /api/v1.0/spaces/{slug}/privacy-notices/{notice_id}/purposes/{purpose_id}
Response: {"message": "Processing purpose deleted"}
Returns 404 when the purpose cannot be found in the space.
Organization RoPA Report
Generate a paginated RoPA report across all spaces in an organization.
GET /api/v1.0/spaces/org/{org_slug}/ropa-report
Query parameters are the same as the space RoPA report:
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 20 | Page size from 1 to 100. |
offset | integer | 0 | Records to skip. |
search | string | - | Search product name, lawful basis, or processing purpose. |
lawful_basis | string | - | Filter by lawful basis value. |
environment | string | - | Filter by product environment. |
sensitivity | string | - | Filter by product sensitivity level. |
mapping_status | string | all | mapped, unmapped, or all. |
Organization RoPA records include space_slug and space_name so clients can route remediation back to the owning space.
Organization RoPA Summary
GET /api/v1.0/spaces/org/{org_slug}/ropa-summary
Returns the same shape as the space summary:
{
"total": 480,
"mapped": 433,
"unmapped": 47,
"missing_basis": 19,
"with_compliance_gap": 11
}
Organization Privacy Notices
List privacy notices aggregated across all spaces in an organization.
GET /api/v1.0/spaces/org/{org_slug}/privacy-notices
The response is the same privacy notice array shape returned by the space listing. space_name is populated when available so clients can identify the source space.
Organization privacy notice endpoints are read-only. Create, update, delete, and purpose changes are performed in the source space.
Organization RoPA Export
GET /api/v1.0/spaces/org/{org_slug}/ropa-export
The response is text/csv with a Content-Disposition filename of ropa-report-{org_slug}-all-spaces.csv. Columns match the space export.
Common Workflows
Create A Notice And Map Products
- Create a notice with
POST /spaces/{slug}/privacy-notices. - Add one or more purposes with
POST /spaces/{slug}/privacy-notices/{notice_id}/purposes. - Update catalog product compliance metadata with a
processing_purpose_id, lawful basis, retention period, and data-subject categories through the Catalog API. - Check
GET /spaces/{slug}/ropa-summaryfor unmapped or missing-basis counts. - Use
GET /spaces/{slug}/ropa-report?mapping_status=unmappedto identify products that still need mapping.
Version A Notice
- Create the new version as
DRAFT. - Add or adjust purposes under the draft notice.
- After legal review, update the new notice to
ACTIVEand setvalid_from. - Update the old notice to
ARCHIVEDand setvalid_to. - Move product mappings to the active notice's purposes where needed.
Export Audit Evidence
Use the space or organization export endpoint for audit packets. Because export endpoints do not accept filters, apply any narrowing in downstream tooling or use the paginated report endpoint when you need filtered JSON.