B BrandLeaf docs

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, change plan, and (since v1.1) delete.

Change a customer's plan

Click Change plan on the user's row (or on Admin → Workspaces → the workspace). Plans belong to workspaces, so a user who owns more than one workspace picks the workspace first. Every plan is listed, inactive and hidden ones too, so a hand-made deal plan can be given to one customer.

  • No payment, right away. The workspace switches to the plan immediately and keeps it until you change it again or the customer buys a plan on their Billing page (that purchase takes over).
  • Trials. A trial that ran out (the upgrade wall) is lifted. Choosing a trial plan starts a fresh trial of that plan's length.
  • Paying customers are refused. A workspace that Stripe, PayPal or Razorpay bills keeps its plan — change it in that gateway, or cancel the subscription first, so the customer is never charged for one plan while using another.
  • The change is recorded in the audit log (billing.plan.changed_by_admin). Under the hood the workspace's plan_subscriptions row gets gateway admin.

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. Transfer ownership at /admin/workspaces/{id} first (or via the workspace's own Members & roles page) 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_at is set to the current timestamp. Subsequent reads via Eloquent's default scope exclude the row.
  • All workspace_users pivot 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 a user_id column are left intact — soft-delete preserves the row, so those joins keep working.
  • An AuditLog entry with action=platform_user.deleted is 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.

Invitation-only installs (disable public sign-ups)

Settings → System → Sign-ups → "Allow public sign-ups" (on by default). Switch it off to run the installation invitation-only — the right mode when the platform serves internal departments rather than the public:

  • The register page redirects to login, and the "create account" links disappear from the auth screens.
  • Team invitations keep working: an email that holds a pending invitation can still create its account — the accept-invitation page links straight to a prefilled register form. Nobody else can.
  • Existing accounts are unaffected either way, and the toggle can be flipped back at any time.