List Users (admin)
This tool is only available when authenticated with an admin-scoped
Phoenix API key. User-scoped keys never see it in tools/list and
receive 403 forbidden_admin_scope if they try to call it directly.
See Admin operations overview for details.
Enumerate every user in the calling org — both active members and unexpired invitations — with role, status, the number of API keys they hold, and their all-time credit consumption. Designed for quarterly seat audits, dormant-user cleanup, and "who's burning credits?" investigations from outside the webapp UI.
Tool key: admin_list_users
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
role | "member" | "admin" | No | – | Filter by role. |
status | "active" | "invited" | No | – | active = a team_membership row exists; invited = an unexpired invitation with status='sent'. |
limit | int (1-500) | No | 100 | Page size. |
cursor | string | No | – | Opaque cursor returned by nextCursor of a prior page. |
Required Integrations
None.
Response
{
"users": [
{
"userId": "9a3a9b40-3a6f-4f0a-9f8e-1b7f0b2c0d10",
"email": "alice@acme.com",
"name": "Alice",
"role": "admin",
"status": "active",
"createdAt": "2026-04-17T02:10:09.740Z",
"apiKeyCount": 2,
"lifetimeCredits": 1234
}
],
"nextCursor": null
}
| Field | Notes |
|---|---|
userId | null for invited-only rows that have no public.users row yet. |
email | Lower-cased canonical form. |
createdAt | team_membership.createdAt for active rows; invitation.sentAt for invited rows. |
apiKeyCount | Excludes system-managed (OAuth/onboarding) keys. |
lifetimeCredits | All-time sum across the user's tool_metering rows in this org. Rounded to 6 decimals. |
Note: the original spec included a
lastSignInAtfield; that column does not exist onpublic.usersand is omitted. If you need sign-in tracking, that's a separate auth-instrumentation request.
Pagination
Page-by-page through the org with cursor:
# Page 1
curl "https://phoenix.hginsights.com/api/admin/users?limit=50"
# → { ..., "nextCursor": "eyJ2IjoxLCJraW5kIjoiYWN0aXZlIiwidGltZ..." }
# Page 2
curl "https://phoenix.hginsights.com/api/admin/users?limit=50&cursor=eyJ2IjoxLCJraW5kIjoiYWN0aXZlIiwidGltZ..."
Cursors are opaque base64url-encoded JSON. They embed a version
discriminator (v: 1) so future cursor-shape changes can be detected
cleanly. A cursor produced by admin_list_users is rejected with
invalid_cursor if passed to a different action.
Use Cases
- Quarterly seat audits:
?role=admin&status=activeto confirm the expected admin set;?status=invitedto find stale invitations. - Credit-spike triage: dump the page, sort by
lifetimeCreditsclient-side, identify the top consumer, then calladmin_get_consumption_by_api_keyto localise the spend to a specific key. - Onboarding dashboards: list invited users + days since
sentAtto surface stuck onboardings.
Example Usage
List the first 100 active members
{
"jsonrpc": "2.0",
"id": "1",
"method": "tools/call",
"params": {
"name": "admin_list_users",
"arguments": { "status": "active" }
}
}
List all admins
{
"jsonrpc": "2.0",
"id": "1",
"method": "tools/call",
"params": {
"name": "admin_list_users",
"arguments": { "role": "admin" }
}
}
How It Works
The org slug is derived from the API key. Inside a single
executeInTenantSchema call:
- Active members are queried from
team_membershipsand deduplicated peruserId(a single user can sit on multiple teams within an org). Role aggregates viaBOOL_OR(role = 'admin')so any admin membership wins; the timestamp is the earliestcreatedAtacross the user's memberships. - Invited rows come from
team_invitationswithstatus='sent' AND expires_at > now(). Same email invited to multiple teams collapses viaGROUP BY lower(email). apiKeyCountandlifetimeCreditsaggregate fromapi_keysandtool_meteringseparately (avoiding a Cartesian fan-out from joining all three at once).
After the tenant queries return, email/name for active rows is
enriched from public.users via a single inArray lookup. No
cross-schema joins.
Each call writes an audit row with action view_users,
metadata = { filter: { role, status }, returnedCount }.
Error codes
| Code | Trigger |
|---|---|
validation_error | Args failed Zod validation. |
invalid_cursor | Cursor is malformed, oversized (>4096 chars), uses an unsupported version, or was produced by a different action. |
forbidden_admin_scope | Key is user-scoped or the user is no longer an org admin. |