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.
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
Without a shared foundation, every team rebuilds these edge cases — differently.
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.
Deletion touches memberships, the user row, and your login provider. A crash mid-way leaves a zombie account that's neither active nor gone.
When privacy processing is invisible, every export request becomes a support ticket. Users need states they can watch, not a spinner.
The mechanism end to end, as implemented in the repository.
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.
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.
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.
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
This walkthrough demonstrates the implemented backend contract without using an account, credentials, or provider calls.
Interactive example
Step 1 of 4: Request accepted
Deterministic sample data only. No account, provider, or network connection is used by the interaction.
Highlights
Accepting a request saves the request, its progress tracker, and its job trigger together, in one step.
Export and deletion run in the background with retries, so a crash mid-way strands nothing.
Every request shows queued, processing, and completed states instead of a spinner and hope.
Engineering guarantees
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.
The boundary above is code, not a claim. These files carry the contract:
src/features/privacy/server.tssrc/features/privacy/jobs.tssrc/registries/data-exports.tsThese capabilities share contracts and boundaries with this one.
The docs describe the same contracts this page demonstrates.