Account deletion and data export
Let users delete their own account (with re-authentication and a grace period) and download their data.
Account deletion and data export
Both features are on by default and appear in the Your data card of the hosted account page. Nothing to build on your side.
Re-authentication
Deleting or exporting needs proof of identity in the request itself, so a stolen session is not enough:
| Account has | The user sends |
|---|---|
| a password | password |
| two-factor | code (authenticator, text message or backup code), or has confirmed with a security key in this session |
| neither (social or passkey sign-in only) | emailCode, from POST /v1/client/me/reauth_code |
GET /v1/client/me/reauth_code returns { password, twoFactor, emailCode } so a UI knows what to ask for. Wrong answers count toward account lockout. Not available while a platform operator is impersonating the user.
Deleting an account
POST /v1/client/me/delete { "password": "…", "code": "…", "emailCode": "…" }
→ { "scheduledFor": "2026-11-07T10:00:00.000Z", "deleted": false }- The account is scheduled for deletion after a grace period (default 14 days,
0to 90;0deletes at once). - Every session is signed out, this device included. Sign-in is refused with
403 account_pending_deletion(after the correct password, so the response never reveals whether an email has an account). - The user gets an email with a single-use cancel link (
/cancel-deletion, token in the URL fragment, stored only as a SHA-256). Using it cancels the deletion; the user then signs in as usual. - When the grace period ends, a purge job (runs every 10 minutes inside the server, safe on several servers) deletes the user and everything that cascades from them, deletes organizations they were the only member of, and removes the detail (IP addresses, browsers) from their audit events. The events themselves remain.
A user who is the only admin of an organization that has other members gets 409 sole_org_admin: they must make someone else an admin, or delete the organization, first.
Webhooks: user.deletion_requested, user.deletion_canceled, then user.deleted when the purge happens. Audit events: user.deletion_requested, user.deletion_canceled, user.deleted.
Settings (backend API)
GET /v1/instance/account_deletion → { "enabled": true, "graceDays": 14 }
PATCH /v1/instance/account_deletion { "enabled": false } // or { "graceDays": 30 }When disabled, POST /v1/client/me/delete returns 403 account_deletion_disabled.
Canceling as a developer
POST /v1/users/{userId}/cancel_deletion → { "id": "usr_…", "canceled": true }canceled is false when nothing was pending. In the SDK: backend.users.cancelDeletion(id).
Data export
POST /v1/client/me/export { "password": "…" }Returns a JSON file (Content-Disposition: attachment, Cache-Control: no-store) with: profile, email addresses, connected accounts, two-factor and passkey metadata, sessions (device and IP, decrypted for the user), organizations, accepted legal versions and their activity log. Fields are decrypted for the user. Never included: password hash, authenticator secret, backup codes, token hashes, or your application's private metadata.
Limit: 3 exports per hour per user. Each export is recorded as user.data_exported in the audit log.
SDK
const need = await client.getReauthRequirements(); // { password, twoFactor, emailCode }
await client.sendReauthCode(); // only when need.emailCode
const data = await client.exportData({ password });
const { scheduledFor } = await client.deleteAccount({ password });
await client.cancelAccountDeletion(tokenFromEmailLink);Email template
The account_deletion template (variables link, date) and reauth_code (code, expires_minutes) can be edited like the others; see Email templates.