---
title: "Update User"
method: PUT
path: "/users/{user_id}"
tags: ["users"]
---

# Update User

`PUT /users/{user_id}`

## 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/{user_id}`
- Auth: none declared

Partial update (admin). Only fields present in the request body are touched.

The ``companies`` replace is **role-preserving**; ``memberships`` sets roles
explicitly. Mutually exclusive (422).

**409 when an operator aims ``is_admin: false`` or ``is_active: false`` at themselves**
(:func:`_refuse_self_removal`), and 409 if the change would leave the instance with no
active administrator at all (:func:`_guard_last_admin`). Nothing re-promotes a demoted
administrator, so this is not recoverable through the product.

Audited, like every other write on somebody else's account. Note the audit records
*that* the account was changed and which fields, not their old and new values: a
password is in scope for this route and an oversight trail is not a place to widen the
blast radius of reading it.

## Path parameters

- `user_id` string, required

## Headers

- `authorization` string, nullable

## Cookies

- `access_token` string, nullable

## Request body

- UserUpdate: ``PUT /users/<id>`` body (admin) — partial update. Fields left unset are not modified (don't reset-to-default; api-contract). Validates with ``model_fields_set`` on the handler side to distinguish "absent" from "explicit null".
  - `first_name` string, nullable
  - `last_name` string, nullable
  - `email` string, email, nullable
  - `phone` string, nullable
  - `password` string, nullable
  - `is_admin` boolean, nullable
  - `is_active` boolean, nullable
  - `companies` string[], nullable
  - `memberships` MembershipIn[], nullable
    - `company_id` string, required
    - `role` 'owner' | 'accountant' | 'member'

## Response `200`

Successful Response

- UserResponse: ``GET/POST/PUT /users/...`` → ``{"user": ...}``.
  - `user` UserOut, required: Serialized user (no secrets). ``companies`` is an array of company ids; ``memberships`` adds the per-membership role.
    - `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
  - `invite_mail_attempted` boolean, nullable

## 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)
