---
title: "List accounts"
method: GET
path: "/v1/admin/users"
tags: ["Admin"]
---

# List accounts

`GET /v1/admin/users`

## Request

- Base URL: `https://api.naturali.ai`
- URL: `https://api.naturali.ai/v1/admin/users`
- Auth: one of:
  - HTTP bearer
  - OAuth 2 (authorization code, authorization URL `https://api.naturali.ai/authorize`, token URL `https://api.naturali.ai/token`, scopes: `mcp:access`)

A cursor page of every account, newest first, each with the plan it is on and the balance of its credit ledger. Needs the `admin` role.
`email` narrows the page to one account. It matches the whole address case-insensitively and takes no wildcards: an operator looking for `Ana@Acme.com` finds a row stored as `ana@acme.com`, and there is no substring search across customers' addresses.
An account with no subscription reads as `free` with a balance of `0`: the absence of a row is the free plan, and the least-privileged rung is also what any unresolvable lookup should answer.

## Query parameters

- `email` string, email
- `limit` integer
- `cursor` string

## Response `200`

A page of accounts.

- AdminUserPage
  - `data` AdminUser[], required
    - `id` string, required: Public user ID (user_ prefix).
    - `email` string, email, required
    - `name` string, nullable
    - `email_verified` boolean, required
    - `roles` string[], required: Roles on naturali itself, not on a project. Empty for almost every account; `admin` marks a naturali operator. Changed only through `PUT /v1/admin/users/{user_id}/roles`, by a caller holding the role that grants each one.
    - `usage_alerts` boolean, required: Whether the account is emailed when its model credit, runs or indexed storage reaches 75%, 90% and 100%. On for a new account.
    - `created_at` string, date-time, required
    - `plan` 'free' | 'pro' | 'business' | 'enterprise', required: The rung in effect: the highest the account held this month. `free` for an account with no subscription. It gates the feature lines and the resource counts of the pricing table.
    - `next_plan` 'free' | 'pro' | 'business' | 'enterprise', required: The rung the account moves to when the month turns; differs from `plan` only while a downgrade is pending.
    - `retention_days` integer, nullable, required: The longest content-retention window this account's projects may keep content for, in days. On a plan whose window a contract sets, this is the figure recorded with the grant; `null` means nothing bounds it. Resolved the same way the refusal is — the widest window held during the current billing cycle.
    - `credit_balance_usd` number, required: Model credit left, in US dollars — the ledger's sum, less the part of a past cycle's allowance that lapsed. `0` for an account that has never been credited, and negative for one that has overspent, which refuses its managed generations.
    - `earned_balance_usd` number, required: Marketplace earnings: the publisher's share of fees its consumers funded, less withdrawals. Never lapses.
    - `paid_balance_usd` number, required: What may fund a marketplace fee: purchased credit plus earnings, less the usage included and granted credit did not cover and the fees paid. Included and granted credit never fund a fee, so a call to a priced installed tool or agent is refused with `402 insufficient_credit` while this is not positive.
    - `spend_reconciled_at` string, date-time, nullable, required: When this account's spend was last read off the meter, which is how current `credit_balance_usd` is. `null` when it has never been read for at least one of the account's projects, or the account has none. The same figure the account reads about itself at `GET /v1/users/me/billing`.
    - `storage` object, required: The account's indexed-storage pool, in the metered gigabytes the ceiling is enforced in — the raw file plus every chunk's text and its embedding vector, so not the size of what you uploaded. Pooled across every project the account pays for; per project, read the `gb_day` component of `GET /v1/projects/{project_id}/usage`. An ingest past `limit` is refused with `403 plan_limit_reached`, `resource: "storage"`, whose `details` carry the same `limit` and `used`.
      - `used` number, required: GB held across the account's projects.
      - `limit` number, nullable, required: GB the plan allows; null where a contract sets the ceiling, or where the plan sets none.
      - `sampled_at` string, date-time, nullable, required: When the **oldest** sample behind `used` was taken — the total is only as current as its stalest part, measured every few minutes per project rather than on this request. Null when a project has never been sampled, or there are none.
  - `next_cursor` string, nullable, required: Pass as `cursor` for the next page; null on the last one.

## Other responses

- `400`: The request was malformed or failed validation.
- `401`: Missing or invalid credentials.
- `403`: The caller does not hold the `admin` role.

---

[API](https://skmtc.dev/naturali/apis/naturali-ai-api.md) · [All operations](https://skmtc.dev/naturali/apis/naturali-ai-api/llms.txt) · [OpenAPI document](https://skmtc.dev/naturali/apis/naturali-ai-api/revisions/e4905b78f422?raw)
