Skip to main content

Users

Platform → Users lists every account on the platform, across all organizations, and is the only place where accounts are created by an operator rather than by self-signup. The column layout and detail panel are summarised in Platform admin → Users; this page covers the operations.

Visible to users with is_staff. Two actions — Delete and Grant / Revoke staff — additionally require is_superuser and are hidden otherwise.

Find a user​

  • Search matches first name, last name, email, or username (case-insensitive substring); results refresh about 300 ms after you stop typing.
  • Status filter: All / Active / Inactive (is_active).
  • Role filter: All roles / Staff only.

The list is newest-signup first, 10 per page by default. There is no org filter in the UI (the API accepts org=<id>); each row shows the org count, with the org names on hover.

Create a user​

+ Create User opens a modal with two tabs.

Single​

First name, last name, email (required), phone, and a Password setup choice:

ModeWhat happens
Send setup link (user sets their own password) — defaultThe account is created with no usable password. A one-time set password link is emailed to the user (event invite) and shown to you in the modal afterwards, so you can copy and share it if email delivery is unreliable.
Set password now (admin-defined)You type a password (minimum 8 characters); the user can sign in immediately.

Username is not asked for — it is derived from the email's local part. The new account is not added to any organization; do that from the org's Members page or by invite link. Creation is audit-logged (user.create, recording whether the password was admin-set and whether the invite email went out).

Bulk​

Type or paste email addresses (comma- or Enter-separated); they become chips. Create N users creates every address in invite mode — each gets an account with no password and a setup link emailed (best-effort). Names are left for the user to fill in when they set their password.

The results view lists every row with its outcome:

OutcomeMeaning
Created, emailed / CreatedAccount made; the second form means the email could not be sent — the setup link is still in the API response
Already existsSkipped — an account with that email exists
error detailMissing email, duplicate within the batch, or a validation error

Each created account is audit-logged as user.create with bulk: true.

An invited user who hasn't set a password yet shows Resend invite in the row menu (driven by pending setup = the account has no usable password — not by active/inactive). It regenerates the one-time link, emails it when mail is configured, and confirms with a toast: Setup link emailed to … or Setup link regenerated … (email not sent). Audit-logged as user.resend_setup_link.

  • Refused for accounts that have already activated — use a password-reset flow for those.
  • Org Owners and Admins of an org the user belongs to can also trigger this (from their Members page); it is not staff-only.

Activate / deactivate​

Edit → Account status → Deactivate / Activate toggles is_active. A deactivated user can no longer sign in and is told by notification (account deactivated); reactivation notifies too. You cannot deactivate yourself. Audit-logged as user.deactivate / user.activate.

Deactivation is the reversible "disable"; it keeps the account, its memberships, and its work.

Verify email​

Mark as verified in the detail panel flips the email-verified flag for users whose mail provider rejects the verify link — see Mark a user as verified.

Grant / revoke staff (superuser only)​

Edit → Platform role → Grant staff / Revoke staff toggles is_staff. The user is notified either way (you are now a platform administrator / access removed). You cannot change your own flag here. Audit-logged as user.staff_grant / user.staff_revoke.

Staff can administer any organisation (settings, members, quotas) but cannot open another org's project data unless they also hold the Full data access grant below.

Grant / revoke full data access (superuser only)​

For a staff user, the detail panel shows Grant full data access / Revoke full data access. When granted, that account may open the projects, tasks, annotations and exports of any organisation — subject to the platform policy at Settings → Policies → "Staff access to other orgs' project data" (default Audited, which logs every such view). Superusers always have it; regular staff never do unless granted. Give it only to the few people who need it. Audit-logged as user.data_access_grant / user.data_access_revoke. Full rationale: Security model → Staff access to other organisations' data.

Delete (superuser only)​

Delete in the row menu opens one modal that shows everything the deletion does and refuses to proceed until you have decided the fate of every organisation the person owns and typed their email address back. Nothing happens in stages: the whole plan is applied together when you press Delete user.

The modal lists, with counts:

  • Removed with the account: the account itself, its sign-in tokens and passkeys, all organisation memberships, its contribution records (validated-work credits and payout lines) and its consent signatures.
  • Kept, detached: the person's annotations stay in their projects with no author; projects they created stay with "created by" cleared; audit-log entries stay, showing the email as it was.

For each organisation the person owns you choose one of:

ChoiceEffect
Transfer ownership to a member you pick from the listThat member becomes Owner and is notified (org.transfer)
Make me the ownerYou become Owner (a membership is added if needed) so you can transfer or delete it deliberately later
Delete the organisationThe organisation and everything in it go with the account (org.delete); the modal states how many projects and annotations that is

The Delete user button stays disabled until every organisation has a choice and the typed email matches. The API behaves the same way: POST /api/platform/users/<id>/delete returns 400 without confirm (the email) and a decision for every owned organisation, so nothing can be left ownerless by any client. Preview data comes from GET /api/platform/users/<id>/delete-preview.

Accessibility: the modal is a labelled dialog; focus moves into it on open and returns to the row on close; Tab stays inside; Escape closes; every control is a native form element with a label; validation and server errors are announced.

You cannot delete yourself. Audit-logged as user.delete with the email snapshotted and the per-organisation decisions.

Why this exists

On 12 September 2026 a sole owner was deleted in a two-step confirm that never mentioned the organisation. It was left ownerless and every page for the one account still pointed at it failed with "Uh oh, something went wrong" until a fix was deployed. The single modal above replaced that flow.

What staff cannot edit​

Personal details — name, email, username, phone — are read-only in the detail panel. Staff can suspend, promote, and manage org memberships, but a user edits their own identity from their own profile.

Gotchas​

  • "Pending" ≠ "inactive". Invited accounts are active with no password; the Status filter won't find them — look for the Resend invite action instead.
  • Setup links are one-shot. Resending invalidates nothing you need, but the user must use the newest link.
  • Bulk create never sets passwords and never adds to an org.
  • Deactivate vs delete. Deactivate to block sign-in reversibly; delete only when the data must go (and note org ownership is reassigned, not deleted).

What's next​