Skip to main content

Users

User listing and per-user tasks

📄️List users (for assignment pickers and member management)

List users (for assignment pickers and member management). Pass `?email=<exact-match>` to resolve ONE user by email — used by the MCP / `pt` CLI assignee resolver. Both modes return the SAME envelope: `{ data: User[] }`. The email mode is a one-or-zero-element ARRAY, never a bare object and never `null`, so this schema describes both modes exactly — a point of repeated confusion in review, since 'single user' describes the lookup semantics, not the wire shape. Used by the MCP / `pt` CLI assignee resolver to translate the INF-125 agent-claim convention email (`agent+<class>@constellation.local`) into a UUID. **The `?email=` mode is strictly tenant-scoped to the caller’s active organisation.** The listing mode returns the tenant’s own user rows (keyed on `identity.users.tenant_id`, so synthetic agent users appear whether or not they hold a membership row — customer tenants carry none; the dogfood tenant keeps deliberate org-scoped ones, DIR-69) PLUS members whose ACTIVE `user_tenant_memberships` row is in this tenant while their user row lives in another — cross-tenant operators (INF-322) — for callers holding FULL directory visibility, which is what makes the listing the assignee-UUID verification set for the MCP / CLI for those callers. Note the listing is not filtered by user status, so soft-deleted and suspended users still appear (the `?email=` lookup does filter them; appended cross-tenant rows require the ACTIVE membership itself). FULL directory visibility means an ACTIVE INTERNAL membership plus an ORG-WIDE (GLOBAL-scope) `projects.read`. Every other caller — a guest, a `projects.read.own`-only reader, or one whose full read comes only from a project/programme-scoped role assignment — receives the tenant’s own user rows, mirroring their narrower `/api/people` window; for them `find_user` can surface a cross-tenant member whose UUID this endpoint does not list (PT-992).