Request account deletion (asynchronous finalize)
Fail-closed account-delete request. Flow:
- Confirmation gate (?confirm=<your-email>).
- Stale-pending outgoing transfers are swept (flipped to expired) so they don't show up as live blockers.
- The server re-runs /account/deletion-preview inside a BEGIN IMMEDIATE transaction. If any blocker remains → 409 with error.blockers/error.ownedAppCount and an ACCOUNT_HAS_* code (see below). The caller resolves blockers via the existing routes (DELETE /apps/:ref, share/revoke, transfer/cancel, etc.) and retries.
- On a clean account: users.status flips to deleting, deletion_requested_at + deletion_blocker_snapshot get stamped, API tokens are revoked server-side in the same transaction (so the caller's next request returns 401), the account.delete_requested audit row is written, and an account_delete job is enqueued. Response: 202 { status: 'deleting', finalizeJobId }.
The finalizer (account_delete job handler) runs AccountDeletionService.runFinalize() off the request path: re-validates blockers; cleans up github_installations, incoming shared_access rows keyed on the user's email, and pending incoming app_transfers to that email; throwing-inserts the account.deleted audit row; then DELETE FROMs the user row gated on status='deleting'. If a blocker reappears between the route's accept and the finalizer running, the finalizer flips status back to active and emits account.delete_aborted — the only path where users.status moves backward.
Error codes (409): ACCOUNT_HAS_APPS, ACCOUNT_HAS_COLLABORATORS, ACCOUNT_HAS_PENDING_INVITES, ACCOUNT_HAS_ACTIVE_PR_PREVIEWS, ACCOUNT_HAS_PENDING_TRANSFERS, ACCOUNT_HAS_DEPLOYS_IN_PROGRESS, ACCOUNT_HAS_ACTIVE_RESTORE. Plus ACCOUNT_DELETION_IN_PROGRESS (concurrent delete or status already flipped). Auth-middleware also raises ACCOUNT_DELETION_IN_PROGRESS (403) for any further request from a deleting user.
Request
- Base URL: https://api.percher.run
- URL: https://api.percher.run/account
- Auth: HTTP bearer
Query parameters
Must match the authenticated user's email address.
Sunset override (only meaningful during read-only mode). Set to forfeit-export-window together with a valid single-use X-Sunset-Forfeit-Grant token header to delete during the sunset window. Both are required; the grant is minted only by the dashboard password-reauth flow, so CLI/MCP clients cannot supply it.
Response
Deletion accepted; finalization runs asynchronously. Body: { status: 'deleting', finalizeJobId: string }. API tokens are revoked server-side in the same transaction — the caller's next request will 401.
Changes
No recorded changes to this operation across all 1 revision of this API.