Skip to content
Sleep at nightTrust and privacy

Data export & deletion

Users can export or delete their data, and it actually finishes.

Your users get self-serve data export and account deletion, built in. The work runs as background jobs that retry until done, so a crash or timeout can't eat a privacy request. Users watch the progress the whole way.

  • 1

    step to safely accept a request

  • 5

    retries per background job

  • 24h

    before a download link expires

The problem this solves

Without a shared foundation, every team rebuilds these edge cases — differently.

The export that dies at 30 seconds

An export built into a web request works until the data outgrows the timeout. Then users click the button, wait, and get nothing, with no record that anything was ever tried.

The half-deleted account

Deletion touches memberships, the user row, and your login provider. A crash mid-way leaves a zombie account that's neither active nor gone.

"Where is my data?" tickets

When privacy processing is invisible, every export request becomes a support ticket. Users need states they can watch, not a spinner.

How it works

The mechanism end to end, as implemented in the repository.

  1. 1

    The request is accepted safely

    The server confirms who's asking from their login, then saves the request, a visible progress run, and the job trigger together in one transaction. Clicking twice still creates just one request.

  2. 2

    A background job picks it up

    The job is queued right after the save, with a scheduled sweeper as backup, so the page never waits on the job system.

  3. 3

    The work runs with visible progress

    The export gathers the user's account, memberships, organizations, projects, and notes into one file in storage, updating the progress states as it goes.

  4. 4

    Delivery closes the loop

    An email delivers a download link that expires after 24 hours. Deletion removes memberships, anonymizes the user, and deletes the login account, and repeats are handled cleanly.

Interactive example

See it working

This walkthrough demonstrates the implemented backend contract without using an account, credentials, or provider calls.

Interactive example

Follow the implemented lifecycle

  1. Request acceptedCurrent
  2. Export preparedWaiting
  3. Artifact storedWaiting
  4. User notifiedWaiting

Step 1 of 4: Request accepted

Deterministic sample data only. No account, provider, or network connection is used by the interaction.

Highlights

Why it matters

Requests can't get lost

Accepting a request saves the request, its progress tracker, and its job trigger together, in one step.

Jobs retry until done

Export and deletion run in the background with retries, so a crash mid-way strands nothing.

Progress users can watch

Every request shows queued, processing, and completed states instead of a spinner and hope.

Engineering guarantees

What this does and does not promise

Safety boundary

This is working software, not legal sign-off. You still need to review data categories, retention windows, and user-facing wording against your own product and jurisdiction.

Implementation evidence

The boundary above is code, not a claim. These files carry the contract:

  • src/features/privacy/server.ts
  • src/features/privacy/jobs.ts
  • src/registries/data-exports.ts
Questions

Frequently asked questions

Build on the data export & deletion foundation

The docs describe the same contracts this page demonstrates.