---
name: privacy-api-design
license: Apache-2.0
description: Design privacy API patterns including data subject API for DSAR endpoints, consent API for preference management, deletion API with cascading delete orchestration, and audit API for compliance reporting. Provides OpenAPI specifications, error handling, rate limiting, and authentication patterns.
metadata:
  metadata:
    author: mukul975
    version: '1.0'
    domain: privacy
    subdomain: privacy-engineering
    tags: privacy-api, dsar-api, consent-api, deletion-api, openapi-specification
---
# Privacy API Design Patterns

## Overview

Privacy APIs provide programmatic interfaces for data subject rights exercise, consent management, data deletion, and audit logging. Well-designed privacy APIs enable organizations to automate compliance workflows, integrate with consent management platforms, and provide data subjects with self-service privacy controls. This skill covers API design patterns, OpenAPI specifications, authentication, and error handling for privacy-critical endpoints.

## API Architecture Overview

```
External Consumers                    Privacy API Gateway                    Backend Services
+------------------+                 +--------------------+                 +------------------+
| Data Subject App |----HTTPS/TLS--->| Authentication     |                 | DSAR Service     |
+------------------+                 | Rate Limiting      |----internal---->| Consent Service  |
                                     | Request Validation |                 | Deletion Service |
+------------------+                 | Audit Logging      |                 | Audit Service    |
| Partner Portal   |----HTTPS/TLS--->| Versioning         |                 | Identity Service |
+------------------+                 +--------------------+                 +------------------+
                                            |
+------------------+                        v
| Internal Systems |----mTLS-------->+--------------------+
+------------------+                 | Privacy Event Bus  |
                                     | (async processing) |
                                     +--------------------+
```

## 1. Data Subject API (DSAR Endpoints)

### OpenAPI Specification

```yaml
openapi: 3.1.0
info:
  title: Cipher Engineering Labs - Data Subject Rights API
  version: 1.0.0
  description: >
    API for data subjects to exercise their privacy rights under GDPR,
    CCPA/CPRA, and other applicable privacy regulations.
  contact:
    name: Privacy Engineering Team
    email: privacy-engineering@cipherengineeringlabs.com

servers:
  - url: https://api.cipherengineeringlabs.com/privacy/v1
    description: Production

security:
  - BearerAuth: []
  - OAuth2: [dsar:read, dsar:write]

paths:
  /dsar/requests:
    post:
      operationId: createDSARRequest
      summary: Submit a new data subject access request
      tags: [DSAR]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DSARCreateRequest'
      responses:
        '202':
          description: Request accepted for processing
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DSARResponse'
          headers:
            X-Request-ID:
              schema:
                type: string
                format: uuid
            Location:
              schema:
                type: string
                format: uri
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimited'

    get:
      operationId: listDSARRequests
      summary: List data subject's own requests
      tags: [DSAR]
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum: [pending, in_progress, completed, rejected]
        - name: type
          in: query
          schema:
            type: string
            enum: [access, deletion, portability, rectification, restriction, objection]
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: per_page
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
      responses:
        '200':
          description: List of DSAR requests
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DSARListResponse'

  /dsar/requests/{requestId}:
    get:
      operationId: getDSARRequest
      summary: Get status and details of a specific DSAR
      tags: [DSAR]
      parameters:
        - name: requestId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: DSAR details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DSARDetailResponse'
        '404':
          $ref: '#/components/responses/NotFound'

  /dsar/requests/{requestId}/download:
    get:
      operationId: downloadDSARData
      summary: Download the data package for a completed access request
      tags: [DSAR]
      parameters:
        - name: requestId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Data package download
          content:
            application/zip:
              schema:
                type: string
                format: binary
          headers:
            Content-Disposition:
              schema:
                type: string
        '404':
          $ref: '#/components/responses/NotFound'
        '410':
          description: Download link has expired

components:
  schemas:
    DSARCreateRequest:
      type: object
      required:
        - requestType
        - email
        - identityVerification
      properties:
        requestType:
          type: string
          enum: [access, deletion, portability, rectification, restriction, objection]
          description: Type of data subject right being exercised
        email:
          type: string
          format: email
          description: Email address associated with the account
        identityVerification:
          type: object
          properties:
            method:
              type: string
              enum: [account_login, email_verification, document_upload]
            verificationToken:
              type: string
          required: [method]
        details:
          type: string
          maxLength: 2000
          description: Additional details about the request
        dataCategories:
          type: array
          items:
            type: string
            enum: [profile, transactions, communications, analytics, marketing, all]
          description: Specific data categories (for access/portability)
        rectificationData:
          type: object
          additionalProperties: true
          description: Corrected data values (for rectification requests)
        preferredFormat:
          type: string
          enum: [json, csv, pdf]
          default: json
          description: Preferred data export format (for access/portability)

    DSARResponse:
      type: object
      properties:
        requestId:
          type: string
          format: uuid
        status:
          type: string
          enum: [pending, identity_verification, in_progress, completed, rejected]
        requestType:
          type: string
        submittedAt:
          type: string
          format: date-time
        estimatedCompletionDate:
          type: string
          format: date-time
        regulatoryDeadline:
          type: string
          format: date-time
          description: Maximum date by which response must be provided

    DSARDetailResponse:
      allOf:
        - $ref: '#/components/schemas/DSARResponse'
        - type: object
          properties:
            statusHistory:
              type: array
              items:
                type: object
                properties:
                  status:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  note:
                    type: string
            downloadAvailable:
              type: boolean
            downloadExpiresAt:
              type: string
              format: date-time

    DSARListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/DSARResponse'
        pagination:
          $ref: '#/components/schemas/Pagination'

    Pagination:
      type: object
      properties:
        page:
          type: integer
        perPage:
          type: integer
        totalPages:
          type: integer
        totalItems:
          type: integer

  responses:
    BadRequest:
      description: Invalid request parameters
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Unauthorized:
      description: Authentication required
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    RateLimited:
      description: Rate limit exceeded
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
      headers:
        Retry-After:
          schema:
            type: integer
        X-RateLimit-Limit:
          schema:
            type: integer
        X-RateLimit-Remaining:
          schema:
            type: integer

  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
    OAuth2:
      type: oauth2
      flows:
        authorizationCode:
          authorizationUrl: https://auth.cipherengineeringlabs.com/oauth2/authorize
          tokenUrl: https://auth.cipherengineeringlabs.com/oauth2/token
          scopes:
            dsar:read: Read DSAR request status
            dsar:write: Submit DSAR requests
            consent:read: Read consent preferences
            consent:write: Update consent preferences
            audit:read: Read audit logs
```

## 2. Consent API

### Consent Preference Management

```yaml
paths:
  /consent/preferences:
    get:
      operationId: getConsentPreferences
      summary: Get current consent preferences for the authenticated user
      tags: [Consent]
      responses:
        '200':
          description: Current consent preferences
          content:
            application/json:
              schema:
                type: object
                properties:
                  subjectId:
                    type: string
                  preferences:
                    type: array
                    items:
                      $ref: '#/components/schemas/ConsentPreference'
                  lastUpdated:
                    type: string
                    format: date-time

    put:
      operationId: updateConsentPreferences
      summary: Update consent preferences (bulk update)
      tags: [Consent]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [preferences]
              properties:
                preferences:
                  type: array
                  items:
                    type: object
                    required: [purposeId, granted]
                    properties:
                      purposeId:
                        type: string
                      granted:
                        type: boolean
                      channels:
                        type: array
                        items:
                          type: string
                          enum: [email, sms, push, phone, postal]
      responses:
        '200':
          description: Preferences updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  updated:
                    type: array
                    items:
                      $ref: '#/components/schemas/ConsentPreference'
                  consentReceiptId:
                    type: string
                    format: uuid
                    description: ID of the issued consent receipt

  /consent/purposes:
    get:
      operationId: listConsentPurposes
      summary: List all available consent purposes
      tags: [Consent]
      responses:
        '200':
          description: List of consent purposes
          content:
            application/json:
              schema:
                type: object
                properties:
                  purposes:
                    type: array
                    items:
                      type: object
                      properties:
                        purposeId:
                          type: string
                        name:
                          type: string
                        description:
                          type: string
                        category:
                          type: string
                          enum: [essential, functional, analytics, marketing]
                        mandatory:
                          type: boolean
                        defaultState:
                          type: boolean
                        dataCategories:
                          type: array
                          items:
                            type: string

  /consent/receipts/{receiptId}:
    get:
      operationId: getConsentReceipt
      summary: Retrieve a specific consent receipt
      tags: [Consent]
      parameters:
        - name: receiptId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Consent receipt
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConsentReceipt'
```

## 3. Deletion API

### Cascading Delete Orchestration

```python
"""
Privacy deletion API with cascading delete orchestration.
Handles deletion requests across multiple backend services
with verification and audit logging.
"""

from dataclasses import dataclass, field
from datetime import datetime, timezone
from enum import Enum
from typing import Optional
import uuid


class DeletionStatus(Enum):
    PENDING = "pending"
    IN_PROGRESS = "in_progress"
    PARTIALLY_COMPLETED = "partially_completed"
    COMPLETED = "completed"
    FAILED = "failed"
    VERIFIED = "verified"


class ServiceDeletionStatus(Enum):
    PENDING = "pending"
    IN_PROGRESS = "in_progress"
    COMPLETED = "completed"
    FAILED = "failed"
    SKIPPED = "skipped"  # For legally exempt data


@dataclass
class DeletionTarget:
    service_name: str
    data_categories: list[str]
    priority: int  # Lower number = delete first
    legal_hold_check: bool = True
    retention_exempt: bool = False  # True if data must be retained (e.g., tax records)


@dataclass
class ServiceDeletionResult:
    service_name: str
    status: ServiceDeletionStatus
    records_deleted: int
    records_retained: int
    retention_reason: Optional[str]
    completed_at: Optional[datetime]
    error_message: Optional[str]


@dataclass
class DeletionRequest:
    request_id: str
    subject_id: str
    status: DeletionStatus
    created_at: datetime
    scope: list[str]  # Data categories to delete
    exclude: list[str]  # Data categories exempt from deletion
    service_results: list[ServiceDeletionResult] = field(default_factory=list)
    verified_at: Optional[datetime] = None
    verification_method: Optional[str] = None


class CascadingDeletionOrchestrator:
    """
    Orchestrate data deletion across multiple backend services
    in the correct order, handling dependencies, legal holds,
    and retention requirements.
    """

    def __init__(self, service_registry: list[DeletionTarget], audit_logger):
        self.targets = sorted(service_registry, key=lambda t: t.priority)
        self.audit = audit_logger

    def execute_deletion(
        self,
        subject_id: str,
        scope: list[str],
        exclude: list[str] = None
    ) -> DeletionRequest:
        """
        Execute cascading deletion across all registered services.

        Args:
            subject_id: The data subject whose data should be deleted
            scope: Data categories to delete (or ["all"])
            exclude: Data categories to exclude from deletion

        Returns:
            DeletionRequest with results from each service
        """
        exclude = exclude or []

        request = DeletionRequest(
            request_id=str(uuid.uuid4()),
            subject_id=subject_id,
            status=DeletionStatus.IN_PROGRESS,
            created_at=datetime.now(timezone.utc),
            scope=scope,
            exclude=exclude
        )

        self.audit.log_deletion_start(request)

        for target in self.targets:
            # Skip if data category not in scope
            if "all" not in scope:
                relevant_categories = set(target.data_categories) & set(scope)
                if not relevant_categories:
                    continue

            # Skip if explicitly excluded
            if set(target.data_categories) & set(exclude):
                result = ServiceDeletionResult(
                    service_name=target.service_name,
                    status=ServiceDeletionStatus.SKIPPED,
                    records_deleted=0,
                    records_retained=0,
                    retention_reason="Excluded by request",
                    completed_at=datetime.now(timezone.utc),
                    error_message=None
                )
                request.service_results.append(result)
                continue

            # Check retention exemptions
            if target.retention_exempt:
                result = ServiceDeletionResult(
                    service_name=target.service_name,
                    status=ServiceDeletionStatus.SKIPPED,
                    records_deleted=0,
                    records_retained=0,
                    retention_reason="Legal retention requirement",
                    completed_at=datetime.now(timezone.utc),
                    error_message=None
                )
                request.service_results.append(result)
                continue

            # Check legal holds
            if target.legal_hold_check:
                hold = self._check_legal_hold(subject_id, target.service_name)
                if hold:
                    result = ServiceDeletionResult(
                        service_name=target.service_name,
                        status=ServiceDeletionStatus.SKIPPED,
                        records_deleted=0,
                        records_retained=0,
                        retention_reason=f"Legal hold: {hold}",
                        completed_at=datetime.now(timezone.utc),
                        error_message=None
                    )
                    request.service_results.append(result)
                    continue

            # Execute deletion for this service
            result = self._delete_from_service(subject_id, target)
            request.service_results.append(result)

        # Determine overall status
        statuses = [r.status for r in request.service_results]
        if all(s in (ServiceDeletionStatus.COMPLETED, ServiceDeletionStatus.SKIPPED) for s in statuses):
            request.status = DeletionStatus.COMPLETED
        elif any(s == ServiceDeletionStatus.FAILED for s in statuses):
            request.status = DeletionStatus.PARTIALLY_COMPLETED
        else:
            request.status = DeletionStatus.COMPLETED

        self.audit.log_deletion_complete(request)
        return request

    def verify_deletion(self, request: DeletionRequest) -> DeletionRequest:
        """
        Verify that data was actually deleted from all services.
        Run after deletion to confirm no data remnants exist.
        """
        for result in request.service_results:
            if result.status == ServiceDeletionStatus.COMPLETED:
                # Attempt to query for the subject's data
                remaining = self._check_data_exists(
                    request.subject_id, result.service_name
                )
                if remaining > 0:
                    result.status = ServiceDeletionStatus.FAILED
                    result.error_message = f"Verification failed: {remaining} records still exist"

        request.verified_at = datetime.now(timezone.utc)
        request.verification_method = "post_deletion_query"

        all_verified = all(
            r.status in (ServiceDeletionStatus.COMPLETED, ServiceDeletionStatus.SKIPPED)
            for r in request.service_results
        )
        request.status = DeletionStatus.VERIFIED if all_verified else DeletionStatus.PARTIALLY_COMPLETED

        self.audit.log_deletion_verification(request)
        return request

    def _check_legal_hold(self, subject_id: str, service: str) -> Optional[str]:
        """Check if any legal hold applies to this subject's data."""
        # Integration point with legal hold management system
        return None  # Override in production with actual legal hold check

    def _delete_from_service(
        self, subject_id: str, target: DeletionTarget
    ) -> ServiceDeletionResult:
        """Execute deletion against a specific backend service."""
        # Integration point with actual backend service deletion API
        return ServiceDeletionResult(
            service_name=target.service_name,
            status=ServiceDeletionStatus.COMPLETED,
            records_deleted=0,
            records_retained=0,
            retention_reason=None,
            completed_at=datetime.now(timezone.utc),
            error_message=None
        )

    def _check_data_exists(self, subject_id: str, service: str) -> int:
        """Check if data still exists for a subject in a service."""
        # Integration point with service data existence check
        return 0
```

## 4. Audit API

### Audit Log Query Endpoints

```yaml
paths:
  /audit/events:
    get:
      operationId: queryAuditEvents
      summary: Query privacy audit events
      tags: [Audit]
      security:
        - OAuth2: [audit:read]
      parameters:
        - name: subjectId
          in: query
          schema:
            type: string
        - name: eventType
          in: query
          schema:
            type: string
            enum: [data_access, consent_change, deletion, dsar_submitted, dsar_completed, breach_detected]
        - name: startDate
          in: query
          required: true
          schema:
            type: string
            format: date-time
        - name: endDate
          in: query
          required: true
          schema:
            type: string
            format: date-time
        - name: actorId
          in: query
          schema:
            type: string
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: perPage
          in: query
          schema:
            type: integer
            default: 50
            maximum: 200
      responses:
        '200':
          description: Audit events
          content:
            application/json:
              schema:
                type: object
                properties:
                  events:
                    type: array
                    items:
                      type: object
                      properties:
                        eventId:
                          type: string
                          format: uuid
                        eventType:
                          type: string
                        timestamp:
                          type: string
                          format: date-time
                        actorId:
                          type: string
                        actorType:
                          type: string
                          enum: [user, system, admin, api_client]
                        subjectId:
                          type: string
                        resource:
                          type: string
                        action:
                          type: string
                        purpose:
                          type: string
                        outcome:
                          type: string
                          enum: [success, failure, denied]
                        details:
                          type: object
                  pagination:
                    $ref: '#/components/schemas/Pagination'

  /audit/reports/compliance:
    get:
      operationId: getComplianceReport
      summary: Generate compliance audit report for a date range
      tags: [Audit]
      parameters:
        - name: startDate
          in: query
          required: true
          schema:
            type: string
            format: date
        - name: endDate
          in: query
          required: true
          schema:
            type: string
            format: date
        - name: format
          in: query
          schema:
            type: string
            enum: [json, pdf, csv]
            default: json
      responses:
        '200':
          description: Compliance report
          content:
            application/json:
              schema:
                type: object
                properties:
                  reportId:
                    type: string
                  generatedAt:
                    type: string
                    format: date-time
                  period:
                    type: object
                    properties:
                      start:
                        type: string
                      end:
                        type: string
                  summary:
                    type: object
                    properties:
                      totalDataAccesses:
                        type: integer
                      totalConsentChanges:
                        type: integer
                      totalDeletions:
                        type: integer
                      totalDSARs:
                        type: integer
                      accessDenials:
                        type: integer
                      policyViolations:
                        type: integer
```

## API Security Patterns

### Authentication and Authorization

| Endpoint Category | Auth Method | Scopes Required | Rate Limit |
|-------------------|-------------|-----------------|------------|
| DSAR (data subject) | OAuth2 PKCE / JWT | dsar:read, dsar:write | 10 req/min |
| DSAR (admin) | OAuth2 Client Credentials | dsar:admin | 100 req/min |
| Consent (data subject) | OAuth2 PKCE / JWT | consent:read, consent:write | 30 req/min |
| Deletion (internal) | mTLS + API key | deletion:execute | 5 req/min |
| Audit (compliance) | OAuth2 Client Credentials | audit:read | 50 req/min |

### Error Response Format

```json
{
  "error": {
    "code": "PRIVACY_001",
    "type": "identity_verification_required",
    "message": "Identity verification is required before processing this request",
    "details": {
      "verificationMethods": ["email_verification", "document_upload"],
      "supportUrl": "https://support.cipherengineeringlabs.com/privacy"
    },
    "requestId": "req_a1b2c3d4",
    "timestamp": "2026-03-14T10:30:00.000Z"
  }
}
```

## References

- OpenAPI Specification 3.1.0
- GDPR Articles 12-22 (Data Subject Rights)
- CCPA/CPRA Sections 1798.100-1798.135
- RFC 6749 — OAuth 2.0 Authorization Framework
- RFC 7519 — JSON Web Token (JWT)
- OWASP API Security Top 10 (2023)
- Google Privacy Sandbox API Design Guidelines
- Apple App Tracking Transparency API Design Patterns
This concludes the detailed specification and best practices for implementing the OpenSkills API with a focus on privacy, security, and compliance. Adhering to these guidelines ensures robust protection of data subject rights, secure handling of sensitive operations, and alignment with global privacy regulations.

For further updates or community contributions, please refer to the official OpenSkills repository and documentation portals. Always keep your integrations up to date with the latest security patches and compliance requirements to maintain trust and reliability.

Thank you for using OpenSkills.
