GDPR delete workflow

How account deletion is implemented and how it leads to data deletion. For compliance and verification.

Last updated: March 2025

Account deletion → data deletion process

  1. User initiates: User requests account deletion (in-app or via support) and confirms. For self-service, the user calls POST /api/compliance/gdpr-delete with body { "confirm": true } while authenticated.
  2. Backend processing: The API (using service role) performs the following in order:
    • Profiles: Email set to null; deleted_at and updated_at set to current time.
    • Projects, reports, experiments, pipelines, datasets: Rows for that user_id are soft-deleted (deleted_at and updated_at set).
    • API keys: Rows for that user are updated with revoked_at set.
    • Auth user: The user record is deleted via Supabase Auth admin.deleteUser(userId), which invalidates all sessions.
  3. What is not modified: Activity and usage logs are append-only and are not updated or deleted by this process. They may be purged later by a retention job (see Data handling). Legal hold or other obligations may require longer retention of some log data.
  4. Verification: After a successful response, the user cannot log in again. Soft-deleted rows are excluded from normal application queries (e.g. WHERE deleted_at IS NULL). Hard deletion or purge of soft-deleted rows can be implemented separately (e.g. scheduled job) if required.

Endpoint

POST /api/compliance/gdpr-delete. Requires an active session (cookie). Body: { "confirm": true }. Without confirm: true the API returns 400. Without a valid session it returns 401.

Export before delete

Users can export their data before requesting deletion via GET /api/compliance/gdpr-export (returns profile, projects, reports, experiments, pipelines, datasets, activity_logs, usage_logs for the authenticated user).

How to test

  • Log in as a test user, create some data, then POST to gdpr-delete with { "confirm": true }. Expect 200 and success; the user cannot log in again.
  • Without confirm: true → 400.
  • Without session → 401.

Related