Users can delete their own account through DELETE /account/profile. The deletion is soft by default, with a configurable grace period that allows the user to restore their account by logging in again.
DELETE /account/profileThe client gets a 204 on delete. No confirmation step, no "are you sure" from the API. That's the client's job if they want one.
Two values live under auth.delete in config/auth.php:
grace_period controls how many days a soft-deleted account can be restored. Set it to 0 to skip the grace period entirely, meaning the account becomes unrecoverable immediately (though it still waits for the prune command to actually remove the data). Defaults to 30.
prune_strategy controls what happens when the scheduled command processes accounts past the grace period. delete hard-deletes the row. anonymize nulls out PII fields and replaces the email with a random hash, preserving referential integrity. Defaults to anonymize. Both values are backed by the AccountPruneStrategy enum.
Login queries users with withTrashed() so soft-deleted accounts are found. If the account is trashed and within the grace period, it's restored and the login proceeds normally. If it's past the grace period or the grace period is 0, the login is rejected with a validation error.
This means the user doesn't need a separate "restore my account" endpoint. They just log in.
users:prune-deleted runs daily via the scheduler. It finds all soft-deleted users whose deleted_at is older than the grace period and applies the configured strategy.
The anonymize strategy replaces the user's name with "Deleted User", generates a random email under @anonymized.local, randomizes the password, removes the avatar from S3, and deletes any remaining tokens. The row stays in the database with deleted_at intact.
Accidental deletions happen. A grace period catches the majority without any operational burden. The prune command handles cleanup automatically, so stale rows don't accumulate indefinitely.
Hard delete is simpler, but if other tables reference the user (orders, audit logs, activity records), cascading deletes can destroy data that has value independent of the user. Anonymizing strips PII while preserving the row for foreign key integrity. For a fresh starter project this distinction barely matters, but the default is set for the direction most projects grow toward.