---
title: "List Users"
method: GET
path: "/users/"
tags: ["users"]
---

# List Users

`GET /users/`

## Request

- Base URL: `https://app.zeno-works.ch` (the document declares no server; this is the origin it was published from)
- URL: `https://app.zeno-works.ch/users/`
- Auth: none declared

List users, optionally searched and paged (admin).

``q`` matches email, first name or last name, case-insensitively. ``total`` counts the
matches before paging, so a client can page without guessing where the list ends.

**``limit`` is unbounded by default, deliberately.** This route carries two eager loads
and returns every account on the instance, which is why the admin surface plan says not
to build a screen on it — but bounding it by default is not free: the status board's
admin check (``tools/status-smoke``) GETs this route with no parameters and asserts every
``ADMIN_USERS`` entry is present, and the ordering is by email, so a default page would
drop admins off the end and fail a production monitor for a reason nobody would connect
to this change. New callers pass a ``limit``; the old contract is unchanged.

## Query parameters

- `q` string, nullable
- `limit` integer, nullable
- `offset` integer

## Headers

- `authorization` string, nullable

## Cookies

- `access_token` string, nullable

## Response `200`

Successful Response

- UserListResponse: ``GET /users/`` → ``{"users": [...], "total": n}``. ``total`` is the number of users matching the search, **before** ``limit``/``offset`` — so a paging client can say "41–60 of 137" and, more importantly, can tell a page that happens to be short from the end of the list. It is added rather than the route being moved onto the shared :class:`Paginated` envelope because ``users`` is a wire name with live consumers outside this repo's SPAs (the status-smoke admin check reads ``body["users"]``), and renaming it to buy consistency would break them for nothing.
  - `users` UserOut[], required
    - `id` string, required
    - `first_name` string, required
    - `last_name` string, required
    - `email` string, email, required
    - `phone` string, nullable
    - `is_admin` boolean, required
    - `is_active` boolean, required
    - `ui_language` string, nullable
    - `companies` string[]
    - `memberships` MembershipOut[]
      - `company_id` string, required
      - `role` 'owner' | 'accountant' | 'member', required
  - `total` integer, required

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/zeno-works/apis/zeno-api.md) · [All operations](https://skmtc.dev/zeno-works/apis/zeno-api/llms.txt) · [OpenAPI document](https://skmtc.dev/zeno-works/apis/zeno-api/revisions/1eb93dde146c?raw)
