# GET /api/v2/users

List users

- MCP id: users.index
- Scope: api:read:users
- Permission: users::read
- Rate cost: 1 unit, read bucket. `view=detail` makes it 3. Adds: `q` +2.
- Cache-Control: private, no-store

Returns the organisation's people, by name A→Z, the order plnnrly's user list uses.

Search by name or email address with `q`, which is the practical way to resolve a colleague to the `user_id` every other collection filters on. Service accounts are never listed. Roles, permissions and teams are not part of this payload and no filter narrows by them. The working week is a detail-view field: send `view=detail` to get `working_hours`. `?include=` is not accepted: this resource expands no relation, and `company_branch` in particular belongs to customers, not to users. Soft-deleted users are excluded by default; `trashed=with` includes them and `trashed=only` returns just the departed, which is how an incremental sync learns somebody left.

## Parameters
- view (string, optional): Which field set to render.
- per_page (integer 1 to 100, optional): Rows per page. At most 25 when `include` names a to-many relation; a larger value is 400 invalid-query-parameter.
- cursor (string, optional): Opaque keyset cursor from a previous page's links.next. Never construct one by hand.
- page (integer 1 to 1000, optional): Opt in to page-number paging: 1-based, at most 1000. Omit it, the default, to page by opaque cursor, which is what links.next hands you. Cannot be combined with cursor. Published only on collections that count their rows on every request.
- filter[birthday] (date (YYYY-MM-DD), optional): Date of birth. `null` selects the users whose birthday was never recorded. Operators: = gte lte null.
- filter[created_at] (timestamp (YYYY-MM-DDThh:mm:ssZ), optional): Creation timestamp. Pair with `sort=id` for a stable full export. Operators: = gte lt.
- filter[date_employed] (date (YYYY-MM-DD), optional): First day of employment. `null` selects the users whose start date was never recorded. Operators: = gte lte null.
- filter[updated_at] (timestamp (YYYY-MM-DDThh:mm:ssZ), optional): Last-write timestamp. Use for incremental sync. Operators: = gte lt.
- q (string, optional): Free-text search over `name`, `email`, in that order of relevance. At most one substring match per request: `q` together with a `contains` filter is 400 invalid-query-parameter.
- sort (string, optional): Up to three keys, comma-separated, each optionally prefixed with `-` for descending.
- trashed (string, optional): Whether soft-deleted rows are included.
- X-Request-Id (header, optional): A correlation id of your own. It is recorded in our logs next to the server's request id but never returned: the response header always carries the server's id. A value that does not match the pattern is discarded silently, never rejected.
- If-None-Match (header, optional): The ETag a read is conditional on. A match is answered 304 with no body.

## Response 200
- id (integer, required): Unique identifier.
- name (string ≤ 255, required): Full name as shown throughout plnnrly. Searchable with `?q=`.
- email (string ≤ 255, required): Work email address, unique within the organisation. Searchable with `?q=`, which is the practical way to resolve a person to an id.
- color (string ≤ 255, required): The user's identifying colour, as a CSS colour string. Used by plnnrly's calendars and avatars; render it or ignore it.
- job_title (string ≤ 255, required): Job title, e.g. Project Manager.
- phone_number (string or null ≤ 255, required): Phone number as entered, in no normalised format.
- timezone (string or null ≤ 255, required): The user's own IANA timezone, e.g. Europe/Amsterdam. Null means the organisation's default applies. Every timestamp in this API is UTC regardless; this is the zone a client renders them in for this person.
- date_employed (date (YYYY-MM-DD) or null, required): First day of employment, or `null` where it was never recorded. A calendar date with no clock, so it is never converted to UTC.
- birthday (date (YYYY-MM-DD) or null, required): Date of birth, or `null` where it was never recorded.
- profitability_target_percentage (integer or null, required): Share of available hours this person is expected to bill, 1 to 100, or `null` where no target is set. Read by the profitability report; a user without a target is still reported on, just not judged against one.
- working_hours (array of object or null, required, detail only): The contracted working week, one entry per shift, in week order. A weekday may carry several entries (a split day is two ranges), and a weekday that is not worked simply has none. Null or empty means no schedule has been recorded.
- work_locations (object, required, detail only): Where the user usually works on each weekday, keyed monday through sunday. The stored pattern lists only the days away from the office, so a weekday it does not name reads as office here. A single day's override from the agenda is not reflected.
  - monday (string, required): Monday.
  - tuesday (string, required): Tuesday.
  - wednesday (string, required): Wednesday.
  - thursday (string, required): Thursday.
  - friday (string, required): Friday.
  - saturday (string, required): Saturday.
  - sunday (string, required): Sunday.
- created_at (timestamp (YYYY-MM-DDThh:mm:ssZ), required): Creation timestamp.
- updated_at (timestamp (YYYY-MM-DDThh:mm:ssZ), required): Last-write timestamp; use for incremental sync.
- deleted_at (timestamp (YYYY-MM-DDThh:mm:ssZ) or null, required): Soft-delete timestamp, `null` while the user is active.
- links (UserLinks, required)
  - self (url, required): The canonical URI of this record.

## Errors
- 403 forbidden-by-policy | insufficient-permission | insufficient-token-scope: Forbidden.
- 304 : Not modified.
- 400 idempotency-key-required | invalid-cursor | invalid-query-parameter | malformed-request | unsupported-parameter: Bad request.
- 401 token-expired | unauthenticated: Unauthenticated.
- 404 endpoint-not-found | resource-not-found: Not found.
- 406 not-acceptable: Not acceptable.
- 429 rate-limit-exceeded: Too many requests.
- 500 internal-error: Server error.
- 503 tenant-unavailable: Service unavailable.

## Example
```bash
curl -G \
  'https://<your-url>/api/v2/users' \
  --data-urlencode 'q=Jansen' \
  -H 'Authorization: Bearer <token>' \
  -H 'Accept: application/json'
```
