Skip to main content
Home›Docs›Security & Compliance›Privacy requests
Security & Compliance

Privacy requests

How to request, track, and download data exports or deletions for your account.

Privacy requests

Version: 1.0
Last Updated: 2026-03-07
Owner: Privacy & Compliance Team
Classification: Internal — Confidential
Related Docs: Data Inventory · Retention Schedule · Consent Model · Subprocessors


1. Overview

This document defines the Data Subject Access Request (DSAR) workflow for the Perceive8 platform, covering data export (right of access / portability) and data deletion (right to erasure / right to be forgotten). These workflows are required for compliance with GDPR, CCPA/CPRA, and other privacy regulations.

Regulatory Timeline Requirements

Regulation Right of Access Right to Deletion Extensions
GDPR (EU) 30 days 30 days +60 days with notification
CCPA/CPRA (California) 45 days 45 days +45 days with notification
PIPEDA (Canada) 30 days 30 days +30 days with notification
UK GDPR 30 days 30 days +60 days with notification

Current Implementation Status

❌ Not Implemented — No DSAR API endpoints, export functionality, or automated deletion cascades exist. All processes described below are proposed designs that need to be built.


2. Proposed API Endpoints

2.1 Privacy API Routes

Method Endpoint Purpose Auth Required
POST /v1/privacy/export Request a data export Yes (user token)
GET /v1/privacy/export/{request_id} Check export status / download Yes (user token)
POST /v1/privacy/delete Request account and data deletion Yes (user token)
GET /v1/privacy/delete/{request_id} Check deletion status Yes (user token)
POST /v1/privacy/delete/cancel Cancel a pending deletion request Yes (user token)
GET /v1/privacy/consents List active consents Yes (user token)
DELETE /v1/privacy/consents/{consent_id} Withdraw a specific consent Yes (user token)

2.2 Admin API Routes

Method Endpoint Purpose Auth Required
GET /v1/admin/privacy/requests List all DSAR requests Yes (admin token)
GET /v1/admin/privacy/requests/{request_id} Get DSAR request details Yes (admin token)
POST /v1/admin/privacy/requests/{request_id}/approve Approve a DSAR request Yes (admin token)
POST /v1/admin/privacy/requests/{request_id}/extend Extend DSAR deadline Yes (admin token)

2.3 Proposed Schema

CREATE TABLE privacy_requests (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID NOT NULL,
    request_type VARCHAR(20) NOT NULL,  -- 'export' or 'delete'
    status VARCHAR(20) NOT NULL DEFAULT 'pending',
        -- pending, processing, completed, failed, cancelled
    requested_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    deadline_at TIMESTAMPTZ NOT NULL,   -- Regulatory deadline
    processing_started_at TIMESTAMPTZ,
    completed_at TIMESTAMPTZ,
    cancelled_at TIMESTAMPTZ,
    export_file_path TEXT,              -- MinIO path for export archive
    export_expires_at TIMESTAMPTZ,      -- Export download expiry
    deletion_log JSONB,                 -- Detailed log of what was deleted
    error_message TEXT,
    admin_notes TEXT,
    ip_address INET,
    user_agent TEXT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX idx_privacy_requests_user ON privacy_requests(user_id);
CREATE INDEX idx_privacy_requests_status ON privacy_requests(status);
CREATE INDEX idx_privacy_requests_deadline ON privacy_requests(deadline_at) WHERE status = 'pending';

3. Data Export Process

3.1 Export Request Flow

User requests export
    │
    ▼
┌─────────────────────┐
│  Validate identity   │
│  (re-authentication) │
└─────────┬───────────┘
          │
          ▼
┌─────────────────────┐
│  Create privacy      │
│  request record      │
│  (type: export)      │
│  Deadline: +30 days  │
└─────────┬───────────┘
          │
          ▼
┌─────────────────────┐
│  Queue export job    │
│  (procrastinate)     │
└─────────┬───────────┘
          │
          ▼
┌─────────────────────┐
│  Collect data from   │
│  all sources         │
│  (see §3.2)          │
└─────────┬───────────┘
          │
          ▼
┌─────────────────────┐
│  Package as ZIP      │
│  archive             │
└─────────┬───────────┘
          │
          ▼
┌─────────────────────┐
│  Upload to MinIO     │
│  (signed URL, 7-day  │
│   expiry)            │
└─────────┬───────────┘
          │
          ▼
┌─────────────────────┐
│  Notify user via     │
│  email               │
└─────────┬───────────┘
          │
          ▼
┌─────────────────────┐
│  User downloads      │
│  export archive      │
└─────────────────────┘

3.2 Data Included in Export

The export archive contains all personal data associated with the user, organized by category:

perceive8-export-{user_id}-{date}/
├── README.md                          # Export manifest and description
├── account/
│   ├── profile.json                   # User profile data
│   ├── consents.json                  # Consent records
│   └── preferences.json              # Account settings
├── audio/
│   ├── {analysis_id}/
│   │   ├── metadata.json             # Upload metadata, timestamps
│   │   ├── original.{ext}            # Original audio file (if still retained)
│   │   └── enhanced.{ext}            # Enhanced audio file (if still retained)
│   └── ...
├── transcripts/
│   ├── {analysis_id}/
│   │   ├── transcript.json           # Full transcript with timestamps
│   │   ├── transcript.txt            # Plain text version
│   │   └── transcript.srt            # SRT subtitle format
│   └── ...
├── speakers/
│   ├── speakers.json                  # Speaker profiles
│   ├── diarization/
│   │   └── {analysis_id}.json        # Diarization segments
│   └── voiceprints/
│       └── voiceprint_profiles.json   # Voiceprint metadata (NOT raw embeddings)
├── analysis/
│   ├── {analysis_id}/
│   │   ├── emotions.json             # Emotion/prosody analysis
│   │   ├── sentiment.json            # Sentiment analysis
│   │   ├── entities.json             # Named entities
│   │   ├── topics.json               # Topic detection
│   │   └── moderation.json           # Content moderation flags
│   └── ...
├── reports/
│   ├── {report_id}/
│   │   ├── report.json               # Report data
│   │   └── findings.json             # Report findings
│   └── ...
├── conversations/
│   ├── {conversation_id}/
│   │   └── messages.json             # Chat history
│   └── ...
├── streaming/
│   ├── sessions.json                  # Stream session history
│   └── alerts.json                    # Stream alerts
├── billing/
│   ├── subscriptions.json             # Subscription history
│   ├── usage.json                     # Usage records
│   └── payments.json                  # Payment references (no card data)
├── api/
│   ├── api_keys.json                  # API key metadata (NOT secrets)
│   ├── oauth_clients.json             # OAuth client metadata (NOT secrets)
│   └── webhooks.json                  # Webhook configurations
└── audit/
    └── audit_log.json                 # User's audit trail

3.3 Export Format Specifications

Data Type Primary Format Additional Formats Notes
Structured data JSON — UTF-8 encoded, pretty-printed
Transcripts JSON TXT, SRT Multiple formats for usability
Audio files Original format — Only if still within retention period
Voiceprints JSON (metadata only) — Raw embeddings excluded (security risk)
API keys JSON (metadata only) — Key hashes and secrets excluded

3.4 Export Exclusions

The following data is excluded from exports for security or legal reasons:

Excluded Data Reason
Password hashes Security — managed by Supabase Auth
API key hashes / secrets Security — would compromise account security
OAuth client secrets Security — would compromise integrations
Webhook signing secrets Security — would compromise webhook security
Raw voiceprint embeddings Security — biometric data that could be misused
System-generated scenario templates Not personal data
Internal processing metadata Not personal data

4. Data Deletion Process

4.1 Deletion Request Flow

User requests deletion
    │
    ▼
┌─────────────────────┐
│  Validate identity   │
│  (re-authentication  │
│   + password confirm) │
└─────────┬───────────┘
          │
          ▼
┌─────────────────────┐
│  Create privacy      │
│  request record      │
│  (type: delete)      │
│  Deadline: +30 days  │
└─────────┬───────────┘
          │
          ▼
┌─────────────────────┐
│  14-day cooling-off  │
│  period              │
│  (user can cancel)   │
└─────────┬───────────┘
          │
          ▼
┌─────────────────────┐
│  Send confirmation   │
│  email with cancel   │
│  link                │
└─────────┬───────────┘
          │
     14 days pass
     (no cancellation)
          │
          ▼
┌─────────────────────┐
│  Execute deletion    │
│  cascade             │
│  (see §4.2)          │
└─────────┬───────────┘
          │
          ▼
┌─────────────────────┐
│  Request provider    │
│  data deletion       │
│  (see §5)            │
└─────────┬───────────┘
          │
          ▼
┌─────────────────────┐
│  Send final          │
│  confirmation email  │
└─────────┬───────────┘
          │
          ▼
┌─────────────────────┐
│  Delete Supabase     │
│  Auth account        │
└─────────────────────┘

4.2 Deletion Cascade Order

Data must be deleted in a specific order to respect foreign key constraints and ensure completeness. The deletion cascade proceeds from leaf tables to root tables:

Phase 1: Derived Data (no dependencies)
├── conversation_messages     (FK: conversation_id)
├── report_findings           (FK: report_id)
├── stream_alerts             (FK: session_id)
├── audio_intelligence_segments (FK: analysis_id)
├── transcript_segments       (FK: analysis_id)
├── diarization_segments      (FK: analysis_id)
└── scenario_questions        (FK: scenario_id)

Phase 2: Secondary Records
├── conversations             (FK: user_id)
├── reports                   (FK: analysis_id)
├── stream_sessions           (FK: user_id)
├── voiceprint_profiles       (FK: speaker_id)
├── speakers                  (FK: user_id)
└── scenarios (user-created)  (FK: user_id)

Phase 3: Core Records
├── analyses                  (FK: user_id)
├── audio_files               (FK: analysis_id) + MinIO objects
└── ChromaDB embeddings       (by user_id metadata filter)

Phase 4: Billing (Anonymize, not delete)
├── subscriptions             → anonymize user_id
├── usage_records             → anonymize user_id
├── minute_packs              → anonymize user_id
└── Stripe customer           → request deletion via Stripe API

Phase 5: Access & Security
├── api_keys                  (FK: user_id)
├── oauth_clients             (FK: user_id)
├── webhooks                  (FK: user_id)
└── user_consents             (FK: user_id)

Phase 6: Audit (Anonymize, not delete)
├── audit_log                 → anonymize user_id, anonymize ip_address
└── privacy_requests          → retain for compliance (anonymize user_id)

Phase 7: Account
└── users                     → hard delete
└── Supabase Auth             → delete auth account

4.3 Deletion Verification

After deletion, a verification step confirms all data has been removed:

# Pseudocode for deletion verification
async def verify_deletion(user_id: str) -> DeletionReport:
    report = DeletionReport()
    
    # Check all tables for remaining user data
    tables_to_check = [
        "users", "analyses", "audio_files", "transcript_segments",
        "diarization_segments", "audio_intelligence_segments",
        "speakers", "voiceprint_profiles", "conversations",
        "conversation_messages", "reports", "report_findings",
        "scenarios", "scenario_questions", "stream_sessions",
        "stream_alerts", "api_keys", "oauth_clients", "webhooks",
        "user_consents"
    ]
    
    for table in tables_to_check:
        count = await db.count(table, user_id=user_id)
        report.add_check(table, count == 0)
    
    # Check MinIO for remaining audio files
    minio_objects = await storage.list_objects(prefix=f"users/{user_id}/")
    report.add_check("minio_audio", len(minio_objects) == 0)
    
    # Check ChromaDB for remaining embeddings
    chroma_docs = await chromadb.query(
        where={"user_id": user_id}, limit=1
    )
    report.add_check("chromadb_embeddings", len(chroma_docs) == 0)
    
    # Check anonymized tables (should have no identifiable user_id)
    for table in ["audit_log", "subscriptions", "usage_records"]:
        count = await db.count(table, user_id=user_id)
        report.add_check(f"{table}_anonymized", count == 0)
    
    return report

4.4 What Is NOT Deleted

Data Reason Treatment
Billing records Legal/tax retention (7 years) Anonymized — user_id replaced with hash
Audit logs Security/compliance retention (2 years) Anonymized — user_id and ip_address replaced
Privacy request records Compliance proof Anonymized — retained to prove deletion was performed
System scenario templates Not personal data Retained
Aggregated analytics Not personal data (anonymized) Retained

5. Provider Data Deletion

When a user requests deletion, we must also request deletion of their data from all third-party providers that processed it. See Subprocessors for provider details.

5.1 Provider Deletion Procedures

OpenAI

Attribute Details
Data to Delete Audio processed by Whisper, text sent for embeddings, chat messages
Deletion Method If zero-retention is enabled: no action needed (data not retained). Otherwise: contact OpenAI support or use DPA deletion clause
API Available ❌ No public deletion API
Manual Process Email [email protected] with user identifier and date range
Expected Timeline 30 days
Verification Written confirmation from OpenAI

Recommended: Enable zero-retention on OpenAI API to eliminate the need for per-user deletion requests.


Pyannote.ai

Attribute Details
Data to Delete Audio processed for diarization and voiceprint extraction
Deletion Method Contact provider directly (no public API)
API Available ❌ Unknown
Manual Process Email support with processing identifiers and date range
Expected Timeline Unknown — must establish in DPA
Verification Written confirmation required

Replicate

Attribute Details
Data to Delete Audio processed for diarization/transcription
Deletion Method Data is ephemeral — deleted after prediction completes
API Available ✅ Prediction deletion API (DELETE /v1/predictions/{id})
Manual Process N/A if ephemeral deletion is confirmed
Expected Timeline Immediate (ephemeral)
Verification Verify via prediction status API

Note: Store Replicate prediction IDs in analyses metadata to enable targeted deletion if needed.


Hume AI

Attribute Details
Data to Delete Audio processed for emotion/prosody analysis
Deletion Method Contact provider directly (no public API)
API Available ❌ Unknown
Manual Process Email support with processing identifiers and date range
Expected Timeline Unknown — must establish in DPA
Verification Written confirmation required

AssemblyAI

Attribute Details
Data to Delete Audio and transcripts processed for content intelligence
Deletion Method Transcript deletion API + audio auto-deletion
API Available ✅ DELETE /v2/transcript/{transcript_id}
Manual Process API-based; support available for bulk requests
Expected Timeline Immediate via API
Verification API returns 200 on successful deletion

Note: Store AssemblyAI transcript IDs in analyses metadata to enable targeted deletion.


Stripe

Attribute Details
Data to Delete Customer record, subscription data, payment history
Deletion Method Stripe Customer deletion API
API Available ✅ DELETE /v1/customers/{customer_id}
Manual Process Dashboard or API
Expected Timeline Immediate (soft delete); permanent per Stripe retention policy
Verification API confirmation

Note: Stripe retains some financial data for legal/regulatory compliance even after customer deletion. This is documented in Stripe's privacy policy.


Supabase Auth

Attribute Details
Data to Delete Auth account (email, password hash, session data)
Deletion Method Supabase Admin API
API Available ✅ DELETE /auth/v1/admin/users/{user_id}
Manual Process Dashboard available
Expected Timeline Immediate
Verification API confirmation

5.2 Provider Deletion Tracking

For each DSAR deletion request, track provider deletion status:

{
  "provider_deletions": {
    "openai": {
      "status": "not_needed",
      "reason": "zero_retention_enabled",
      "verified_at": null
    },
    "pyannote": {
      "status": "requested",
      "requested_at": "2026-03-07T12:00:00Z",
      "reference": "support-ticket-12345",
      "verified_at": null
    },
    "replicate": {
      "status": "completed",
      "reason": "ephemeral_processing",
      "verified_at": "2026-03-07T12:00:00Z"
    },
    "hume": {
      "status": "requested",
      "requested_at": "2026-03-07T12:00:00Z",
      "reference": "support-ticket-67890",
      "verified_at": null
    },
    "assemblyai": {
      "status": "completed",
      "deleted_transcript_ids": ["abc123", "def456"],
      "verified_at": "2026-03-07T12:01:00Z"
    },
    "stripe": {
      "status": "completed",
      "deleted_customer_id": "cus_xxx",
      "verified_at": "2026-03-07T12:02:00Z"
    },
    "supabase_auth": {
      "status": "completed",
      "verified_at": "2026-03-07T12:03:00Z"
    }
  }
}

6. Account Deletion Workflow (Step-by-Step)

6.1 User-Initiated Account Deletion

Step 1: User requests deletion

  • User navigates to Account Settings → Delete Account
  • UI displays warning about data that will be deleted
  • UI lists data that will be retained (anonymized billing, audit logs)
  • User must re-enter password to confirm

Step 2: Confirmation email

  • System sends email with:
    • Confirmation of deletion request
    • 14-day cooling-off period notice
    • Link to cancel deletion
    • Summary of what will be deleted

Step 3: Cooling-off period (14 days)

  • Account remains active but flagged for deletion
  • User can cancel at any time via email link or account settings
  • Daily reminder emails at day 7 and day 13
  • If user logs in during this period, show banner with cancel option

Step 4: Deletion execution

  • After 14 days with no cancellation:
    • Revoke all active sessions
    • Execute deletion cascade (§4.2)
    • Request provider deletions (§5)
    • Anonymize billing and audit records
    • Delete Supabase Auth account

Step 5: Final confirmation

  • Send final email confirming deletion is complete
  • Include list of providers where deletion was requested
  • Note any providers where deletion is still pending
  • Provide contact email for questions

Step 6: Verification

  • Run deletion verification (§4.3)
  • Log results in privacy_requests.deletion_log
  • Flag any failures for manual review

6.2 Admin-Initiated Account Deletion

Admins can initiate deletion on behalf of users (e.g., for DSAR requests received via email):

  1. Admin creates DSAR request via admin API
  2. System verifies user identity (admin must confirm)
  3. No cooling-off period (already verified externally)
  4. Same deletion cascade as user-initiated
  5. Admin receives deletion report

7. Right to Be Forgotten — Special Considerations

7.1 Third-Party Recordings

When a recording participant (not the uploader) requests deletion:

Scenario Action
Participant identified by name in speakers table Delete speaker record and associated voiceprint
Participant's voice in audio file Cannot selectively delete one voice from audio — must delete entire audio file
Participant's speech in transcript Delete transcript segments attributed to that speaker
Participant's emotions in analysis Delete emotion analysis segments for that speaker

⚠️ Complex Case: Deleting a non-user participant's data may require deleting or redacting portions of another user's analysis. This requires careful handling and user notification.

7.2 Derived Data

Original Data Derived Data Deletion Required
Audio file Transcript Yes — if user requests audio deletion, offer transcript deletion
Transcript Embeddings (ChromaDB) Yes — embeddings derived from deleted transcripts must be removed
Transcript Report findings (evidence) Yes — evidence text from deleted transcripts must be removed
Audio Voiceprint profile Yes — voiceprints must be deleted with source audio
Audio Emotion analysis Yes — emotion data must be deleted with source audio

7.3 Backup and Disaster Recovery

Consideration Policy
Database backups Deleted data may persist in backups for up to 30 days
MinIO backups Audio files may persist in backups for up to 30 days
Log files Application logs may contain PII; rotated every 7 days
Provider backups Subject to provider retention policies

ℹ️ GDPR Position: Backup retention of deleted data is generally acceptable if: (1) backups are encrypted, (2) retention is time-limited, (3) data is not actively processed from backups, and (4) the policy is documented and disclosed.


8. Billing Data Exception

8.1 Legal Retention Override

Billing data is subject to legal retention requirements that override the right to deletion:

Jurisdiction Retention Requirement Basis
US (IRS) 7 years Tax record retention
EU (VAT) 7–10 years (varies by country) VAT directive
UK (HMRC) 6 years Tax record retention

8.2 Anonymization Approach

Instead of deleting billing records, they are anonymized:

-- Anonymize billing records for deleted user
UPDATE subscriptions 
SET user_id = 'DELETED-' || md5(user_id::text)::uuid
WHERE user_id = :user_id;

UPDATE usage_records 
SET user_id = 'DELETED-' || md5(user_id::text)::uuid
WHERE user_id = :user_id;

UPDATE minute_packs 
SET user_id = 'DELETED-' || md5(user_id::text)::uuid
WHERE user_id = :user_id;

8.3 User Communication

Users requesting deletion are informed:

  • Billing records will be anonymized (not deleted) for legal compliance
  • Anonymized records cannot be linked back to their identity
  • Records will be fully deleted after the legal retention period (7 years)
  • Stripe customer record will be deleted (Stripe manages their own legal retention)

9. Implementation Status

9.1 Component Status

Component Status Priority
privacy_requests database table ❌ Not Implemented 🔴 Critical
POST /v1/privacy/export endpoint ❌ Not Implemented 🔴 Critical
GET /v1/privacy/export/{request_id} endpoint ❌ Not Implemented 🔴 Critical
POST /v1/privacy/delete endpoint ❌ Not Implemented 🔴 Critical
GET /v1/privacy/delete/{request_id} endpoint ❌ Not Implemented 🔴 Critical
POST /v1/privacy/delete/cancel endpoint ❌ Not Implemented 🔴 Critical
Export data collection job ❌ Not Implemented 🔴 Critical
Export ZIP packaging ❌ Not Implemented 🔴 Critical
Deletion cascade logic ❌ Not Implemented 🔴 Critical
ChromaDB deletion by user ❌ Not Implemented 🔴 Critical
MinIO object deletion by user ❌ Not Implemented 🔴 Critical
Billing record anonymization ❌ Not Implemented 🟡 High
Audit log anonymization ❌ Not Implemented 🟡 High
Provider deletion requests ❌ Not Implemented 🟡 High
Provider deletion tracking ❌ Not Implemented 🟡 High
Deletion verification job ❌ Not Implemented 🟡 High
Cooling-off period logic ❌ Not Implemented 🟡 High
Email notifications (export ready, deletion confirmation) ❌ Not Implemented 🟡 High
Admin DSAR dashboard ❌ Not Implemented 🟢 Standard
Non-user participant deletion ❌ Not Implemented 🟢 Standard
Consent management endpoints ❌ Not Implemented 🟡 High

9.2 Implementation Roadmap

Phase 1 — Core DSAR Infrastructure (Weeks 1–3):

  1. Create privacy_requests database table (Alembic migration)
  2. Implement POST /v1/privacy/export and export job
  3. Implement POST /v1/privacy/delete and deletion cascade
  4. Add ChromaDB deletion by user metadata
  5. Add MinIO object deletion by user prefix

Phase 2 — Provider Integration (Weeks 3–5): 6. Implement AssemblyAI transcript deletion (API available) 7. Implement Stripe customer deletion (API available) 8. Implement Supabase Auth account deletion (API available) 9. Create manual process for OpenAI, Pyannote, Hume deletion requests 10. Build provider deletion tracking

Phase 3 — Polish & Compliance (Weeks 5–7): 11. Implement cooling-off period with email notifications 12. Build deletion verification job 13. Implement billing record anonymization 14. Implement audit log anonymization 15. Build admin DSAR dashboard

Phase 4 — Advanced Features (Weeks 7–9): 16. Non-user participant deletion workflow 17. Consent management API endpoints 18. Automated DSAR deadline monitoring and alerts 19. DSAR metrics and reporting


10. Testing Requirements

10.1 Export Testing

  • Export includes all user data from all tables
  • Export excludes security-sensitive data (hashes, secrets)
  • Export ZIP is properly structured and readable
  • Export download link expires after 7 days
  • Export works for users with no data (empty export)
  • Export works for users with large datasets (>100 analyses)
  • Concurrent export requests are handled correctly

10.2 Deletion Testing

  • Deletion cascade removes data from all tables
  • Deletion cascade respects foreign key order
  • ChromaDB embeddings are deleted
  • MinIO audio files are deleted
  • Billing records are anonymized (not deleted)
  • Audit logs are anonymized (not deleted)
  • Deletion verification passes after cascade
  • Cooling-off period cancellation works
  • Provider deletion requests are tracked
  • Re-registration after deletion works (no orphaned data conflicts)

10.3 Edge Cases

  • User with active subscription requests deletion (cancel subscription first)
  • User with pending analyses requests deletion (wait or cancel analyses)
  • User with active streaming session requests deletion (end session first)
  • Multiple deletion requests from same user
  • Deletion request during export processing
  • Provider deletion failure handling and retry

11. Revision History

Version Date Author Changes
1.0 2026-03-07 Privacy & Compliance Team Initial DSAR workflow documentation