Platform admin
Users (incl. delete)
/admin/users lists every user account on the platform —
super-admins, customers, and members invited into any workspace. The
page supports search by name/email, role promotion, BYOK per-user
overrides, edit, impersonate, and (since v1.1) delete.
Delete
Super-admin only. Click the Delete button on any user row (the button is hidden on your own row). A confirm dialog spells out the consequence — they lose access to every workspace and the row is soft-deleted for audit.
Safety rails
The controller refuses four cases:
- Self-delete — you cannot delete the account you are signed into. Would lock you out of admin.
- Last super-admin — defence-in-depth check; the controller refuses if the target is a super-admin and no other live super-admin exists.
-
Workspace owner — if the user still owns one or
more workspaces, the delete returns an error toast naming the
count (its text says "Transfer ownership first"). There is no
ownership transfer anywhere in the app: neither
/admin/workspaces/{id}nor the workspace's own Team page can change the owner. The only way through is to delete those workspaces first (from/admin/workspaces) and retry. -
Non-super-admin actor — the route is in the
platform-admin group; a customer hitting
DELETE /admin/users/{id}directly returns 404 to hide the endpoint's existence.
What soft-delete touches
-
users.deleted_atis set to the current timestamp. Subsequent reads via Eloquent's default scope exclude the row. -
All
workspace_userspivot rows for the user are deleted so a re-invited account doesn't collide on the unique composite key. -
Foreign-key references in
conversations,leads,audit_logs, and other tables with auser_idcolumn are left intact — soft-delete preserves the row, so those joins keep working. -
An
AuditLogentry withaction=platform_user.deletedis written under the acting admin's default workspace, including the deleted email + role for forensic recovery.
Customer-initiated deletion (different code path)
When a customer deletes their own account from
/settings/profile, the row is
hard-deleted via forceDelete() — a
customer-initiated deletion is treated as a GDPR right-to-erasure
request, so the data goes away for real. Only the platform-admin
delete is soft.
Restoring a soft-deleted user
There is no admin UI for restore yet. To bring back a soft-deleted account run:
php artisan tinker --execute 'App\Models\User::withTrashed()->where("email","x@y.com")->first()->restore();'
The user's workspace memberships are NOT restored — they need a
fresh invitation. If the original workspace is gone too,
default_workspace_id will be stale and the next visit
redirects them through the standard onboarding flow.