Deleting accounts
Let people delete their own account after a grace period you set, and see what a deletion removes and what it keeps.
People can delete their own account from the Security screen of your app's settings. They get 7 days to change their mind unless you set another grace period, and then their personal data is scrubbed. Not soft-deleted with every field still sitting in the row: the name, address, phone, sign-in methods and devices go. The user record itself stays, renamed "Deleted user", so the activity and audit rows that point at it still resolve to something.
Step by step, with screenshots: The account security screens.
import { useSaaSAuth } from '@buildbase/sdk/react';
function DeletionNotice() {
const { user, openWorkspaceSettings } = useSaaSAuth();
if (!user?.deleteAfter) return null;
const date = new Date(user.deleteAfter).toLocaleDateString();
return (
<p>
Your account will be deleted on {date}.{' '}
<button onClick={() => openWorkspaceSettings('security')}>Cancel</button>
</p>
);
}What people see
The Security screen ends with a Delete account section.
- A typed confirmation. The person types the word the section asks for:
DELETE, or its translation in their language. - The warning names your grace period above it ("You have 7 days to change your mind"), or, at 0, says the account goes straight away and they will be signed out.
- A recent sign-in. A session older than 10 minutes is asked to confirm it's you first. A guest is not asked.
- Every other session ends the moment they ask. This one stays signed in, so they can come back and cancel.
- Until the date, the section shows when the deletion runs, with Cancel deletion. Signing in during the grace period is allowed, for exactly that reason.
- With a grace period of 0, the account is deleted at once and this session ends with it.
Hide the section with ui.settings.security.deleteAccount: false if your app handles deletion its own way.
The grace period
Set it under User Management → Access → Authentication → Security, in the Account deletion card: from 0 to 30 days, and 7 when you never set it. 0 deletes the moment the person asks. It is stored as security.accountDeletionGraceDays on the auth config.
A sweep runs every 10 minutes and deletes each account whose date has passed.
Workspaces they created
A workspace with other people in it cannot vanish because one of them left. Deleting is refused with 409 owns_workspaces while the person created a workspace that someone else is a member of, or that has a subscription. The refusal names each one (workspaces: [{ id, name }]) and the Security screen lists them.
Nothing hands a workspace to someone else, so the person clears the way themselves: they remove the other members and cancel the subscription, or delete the workspace, then ask again. The refusal's message says so, naming the workspace when there is one:
The workspace "Acme" has other members or a subscription, so it cannot be deleted with this account. Remove the other members and cancel the subscription, or delete the workspace, then try again.
With several, it counts them instead: "3 workspaces have other members or a subscription, so they cannot be deleted with this account."
A workspace they created and use alone is deleted with the account, and their memberships in other people's workspaces end.
The rule is checked again when the grace period runs out. If someone joined one of their workspaces in the meantime, the deletion is held, logged and tried again at the next sweep. It stays scheduled until it goes through or the person cancels.
What goes and what stays
Deleted outright:
- passwords and every other sign-in record, connected accounts and wallets, passkeys and authenticator apps
- devices and sessions, so the person is signed out everywhere
- agent consents and OAuth refresh tokens
- push subscriptions and preferences
- their audience contact and its list memberships
Cleared from the user record: the phone, username, image, tags, attributes, custom claims, external id, Stripe customer id, ad attribution, language, country, currency and timezone. The name becomes "Deleted user" and the address <userId>@deleted.invalid, which is unique and can never receive mail.
Kept: activity, email history and audit records. They are keyed by the user ID, and the person behind it is gone.
Deleting a user from the console
An admin can delete a user from their page in the console with Delete, then Delete user now. That one is immediate, with no grace period: the grace period is for a person who changed their mind, and this person never asked. It runs the same scrub under the same workspace rule, emits user.deleted with by: 'admin', and writes a user.deleted row to the security audit log.
Over the API it is DELETE /api/users/:id, with an org API token whose role can delete users. It answers { success: true }, or 409 owns_workspaces with the workspaces.
Events
| Event | When | data |
|---|---|---|
user.deletion_scheduled | The person asked, with a grace period | actorId, userId, deleteAfter |
user.deletion_cancelled | They cancelled before the date | actorId, userId |
user.deleted | The account was scrubbed | actorId, userId, by |
by is self (the person asked, at once or after the grace period), admin (deleted from the console) or guest_expired (a guest left idle too long). With a grace period of 0 there is no user.deletion_scheduled, only user.deleted.
None of them carries the old address, because by then there is none to send. Match on userId. The SDK types the payload as UserDeletionWebhookData. Subscribe to any of them as a webhook event.
The API
For a screen of your own, the same calls are on WorkspaceApi:
const { deleteAfter, deleted } = await api.requestAccountDeletion();
// deleted is true when your app has no grace period: the account is gone.
await api.cancelAccountDeletion(); // { cancelled }| Route | Answers |
|---|---|
POST /api/v1/public/account/delete | { deleteAfter, deleted }. deleteAfter is null when the account was deleted at once |
POST /api/v1/public/account/delete/cancel | { cancelled }, false when nothing was scheduled |
| Refusal | When |
|---|---|
403 reauth_required | The session is not recent. See Confirm it's you |
409 owns_workspaces | They created a workspace that has other members or a subscription. Carries workspaces |
403 read_only_session | An app owner is signed in as the person, read-only |
While a deletion is scheduled, the profile and useSaaSAuth().user carry deleteAfter, ISO 8601.
Next steps
- Confirm it's you - the recent sign-in a deletion asks for.
- Guest users - guests left idle are removed by the same scrub.
- Webhooks - subscribe to the three deletion events.