Managing users
The Users page sits at Global Admin → Users and is the answer to “who are all the users on this OttoTester instance, and what do they have access to?” It is the surface to reach for before acting on any user-related support ticket.
It is intentionally cross-tenant: the directory includes every user from every organization, plus orphans (users who have signed up but are not yet attached to any org). This is by design — the platform admin who needs this view needs to be able to see across tenants. The endpoint is gated to platform admins only, and every list and detail view is logged in the audit trail.
The Users table
Section titled “The Users table”Each row shows:
- Email — the user’s account email. An Orphan badge is shown when the user has zero organization memberships.
- Name — the user’s display name (when set).
- Created — when the user signed up.
- Last sign-in — the most recent session start. Never when the user has never signed in.
- Primary org — the alphabetically-first organization the user belongs to. Empty for orphans.
- Memberships — the count of organization memberships.
Click any row to open the detail modal.
Filters
Section titled “Filters”The filter bar above the table narrows the directory. All filter state is reflected in the URL — you can share a deep link to a filtered view.
Search
Section titled “Search”Substring match against both email and name, case-insensitive. Use this to find a specific user by partial email, or to scope to a domain (@acme.com).
Sign-in recency
Section titled “Sign-in recency”A drop-down with the common ranges:
- Signed in within 7 / 30 / 90 days — users who have an active or recently-active session.
- No sign-in for 30+ / 90+ days — dormant accounts. A useful starting point for “who hasn’t logged in lately on this organization.”
- Never signed in — users who completed sign-up but never started a session. Catches invitations that were accepted but never used.
Provider
Section titled “Provider”The OAuth provider on the user’s account:
- Email / password — the default email+password sign-up flow.
- Google / GitHub — users who signed up via the OAuth provider.
A user may have multiple providers — selecting one filters to users with at least one account row for that provider.
Orphans only
Section titled “Orphans only”Toggle to show only users with zero organization memberships. These are accounts that have signed up but haven’t been invited into any org. Useful for catching:
- Self-signups that never got onboarded.
- Users whose only org membership was revoked (and who haven’t been re-invited).
- Test accounts that never got cleaned up.
The detail modal
Section titled “The detail modal”Clicking any row opens the user’s full profile and membership graph.
Profile
Section titled “Profile”- User id — the internal database id (useful for cross-referencing audit log rows).
- Email verified — whether the user has verified the email on their account.
- Created / Last sign-in — same fields as the table.
- Providers — the full list of OAuth providers on the user’s account.
- Superadmin — whether the user is a platform admin (manage via Global Admin → Admins).
- Suspended — always No in v1. Suspension will land in a later release.
Organizations
Section titled “Organizations”Every organization the user is a member of, with their role in each. The table includes orgs that were soft-deleted after the user joined — investigators can still see what access the user had, even if the org no longer exists.
Workspaces
Section titled “Workspaces”Every workspace the user has a direct membership in, with their role. Includes the parent organization name so the workspace’s tenancy is obvious at a glance.
Team-mediated workspace reach (a user who has access only via team grant) is not surfaced in v1 — the Users page shows direct grants only. To audit team-mediated reach, use the workspace’s own Members tab.
Why list and detail views are audited
Section titled “Why list and detail views are audited”Every list-page load and every detail-modal open writes an audit row:
user.list_viewed— meta carries the resolved filter shape and the count of rows returned.user.detail_viewed— target is the inspected user id.
This is intentional. Cross-tenant visibility is a privileged operation, and the Users page is the natural surface a platform admin reaches for before acting on a user-related ticket (escalating a support case, investigating a flagged account, preparing a suspension). The audit trail captures that “inspect before act” chronology so compliance reviews can reconstruct who looked at whom and when.
The audit log itself is the only Global Admin surface that does not audit reads — and only because logging audit-log reads into the same collection would recurse on every page-2 load.
Out of scope in v1
Section titled “Out of scope in v1”The page is deliberately read-only in v1. The following actions are planned for a later release:
- Suspend / unsuspend a user — locking their next login attempt.
- Force log-out — invalidating every active session on the user’s account.
- Password reset — issuing an admin-triggered reset email.
- Impersonate — a time-boxed assumed-identity session, with the full impersonation audit trail.
The Suspended field is already in the wire shape so the UI doesn’t change when these land — the only difference will be a row of action buttons appearing in the detail modal.
Frequently asked
Section titled “Frequently asked”Why does a user appear in the list but with “Never” as last sign-in? They completed sign-up but never started a session. Common causes: an invitation was accepted by clicking the verification link but the user closed the tab before signing in; an OAuth account was created but the user immediately abandoned. The user is real but inactive.
Why is an org shown in the detail modal that I thought was deleted? The detail view includes soft-deleted orgs so an investigator can see “the user used to have access to this org” even after the org is gone. The org will still appear in the list until it’s hard-deleted at the end of the recovery window.
The URL has ?orgId=... or ?role=... — where are those filter controls? The backend supports a richer set of filters than the v1 UI exposes. They’re deep-linkable via URL params (paste a URL with ?orgId=...&role=admin and the table narrows accordingly). Surfacing them as UI controls is a later iteration.
Why isn’t there a “primary org” picker? The “Primary org” column on the table is a deterministic alphabetically-first pick — it does NOT presuppose any “default org” semantics. Use the detail modal to see all orgs the user belongs to.

