# Updates the details and settings for a specific administrative user

`PUT /api/users/{username}`

- **Products:** Operator (`FOH`)
- **Features:** Front of House Generic Access (`FOHGENERIC`)

HTML version: [Updates the details and settings for a specific administrative user](https://support.gladstonesoftware.cloud/reference/users/updates-the-details-and-settings-for-a-specific-administrative-user)

## Overview

Replaces the profile, site assignment, role and account-status flags of one staff (back-office) user. Returns `204 No Content` with no body.

This is a full replacement, not a patch. Every field you send is written, and the required fields must be present on every call — so read the account first with [Retrieves detailed information for a specific administrative user](retrieves-detailed-information-for-a-specific-administrative-user.md) and send back the current values of everything you are not changing, or you will silently blank them.

Passwords are not changed here. Use [Updates the password for a specific user account](updates-the-password-for-a-specific-user-account.md) for that, and [Updates a user's password reset flag for account security management](updates-a-users-password-reset-flag-for-account-security-management.md) if you only want to force a reset — although `isPasswordResetFlagSet` on this request writes the same flag.

Two side effects are worth knowing before you call. Changing `isLocked` from `true` to `false` also resets `failedLoginAttemptsCount` to `0`, which is how you release a locked-out user. And `preferredSiteId` is not a field of the account: it is written through as the user preference named `PreferredSiteId`, the same store the endpoints under `/api/users/preference/{preferenceId}` read and write.

## Endpoint

`PUT {base_url}/api/users/{username}`

`{base_url}` is the environment URL issued to your integration. See [Authenticating with Gladstone APIs](../../guides/authenticating-with-gladstone-apis.md).

## Path parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `username` | string | Yes | The sign-in name of the account to update. It must be character-for-character identical to `username` in the request body, or the call fails with `400` and `The username in the request: {username} and the one in route: {username} do not match`. This endpoint cannot rename an account. |

## Request body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `username` | string | Yes | Must repeat the `username` from the path exactly; the two are compared before anything else and a mismatch fails with `400`. It is not a new name for the account — renaming is not supported here. Rejected with `400` if blank or longer than 8 characters. |
| `firstName` | string | Yes | The user's given name. Used to rebuild `displayName` when you send that blank. Rejected with `400` if blank or longer than 25 characters. |
| `lastName` | string | Yes | The user's family name. Used to rebuild `displayName` when you send that blank. Rejected with `400` if blank or longer than 25 characters. |
| `displayName` | string | No | The name shown to other staff in the back office. Rejected with `400` above 50 characters. Sending it blank or omitting it does not leave the current value alone — it resets the stored name to `"{firstName} {lastName}"`. |
| `email` | string | No | The account's contact address. Rejected with `400` above 255 characters, and — when non-empty — validated against an RFC-style address pattern, failing with `The email in the request: {email} is invalid`. Send it blank or omit it to clear the stored address; the format check is skipped in that case. |
| `cardId` | string | No | The identifier on the physical staff card this user presents at a reader or till. Rejected with `400` above 50 characters. Omitting it clears the stored card, so resend the current value if the user still has a card. |
| `siteGroupId` | string | Yes | The site group the user belongs to, taken from `id` in [Retrieves all site groups with their configuration details](../configuration/retrieves-all-site-groups-with-their-configuration-details.md). It must resolve to an existing group or the call fails with `404` — note `404`, not `400` — and `The site group in the request: {siteGroupId} could not be found`. It also constrains `preferredSiteId`. Rejected with `400` if blank. |
| `preferredSiteId` | string | Yes | The site the user works at by default. It must be one of the sites inside `siteGroupId`, compared case-insensitively, or the call fails with `400` and `The preferred site Id: {preferredSiteId} is not contained in the given site group`. Written through as the user preference named `PreferredSiteId` rather than onto the account. Rejected with `400` if blank. |
| `avatarId` | string | No | The profile image the back office shows for this user. Rejected with `400` above 25 characters. On [ObjectId-role tenants](retrieves-all-administrative-users-configured-in-the-system.md), sending it blank resets the saved value to the literal `default`. On numeric-role tenants it is not saved at all. |
| `isActive` | boolean | Yes | Whether the account may be used. Send `false` to suspend a user while keeping their record and username — a reversible alternative to [Permanently deletes a user account from the system](permanently-deletes-a-user-account-from-the-system.md). Because the field is a non-nullable boolean, omitting it reads as `false` and deactivates the account, so always send it explicitly. |
| `isLocked` | boolean | No | Whether the account is locked out of signing in. Sending `false` for a user who was locked also resets `failedLoginAttemptsCount` to `0`, which is how you release a user locked out by repeated failed sign-ins. Omitting it reads as `false` and therefore unlocks the account. |
| `isPasswordResetFlagSet` | boolean | No | Whether the user must set a new password at their next sign-in. Writes the same flag as [Updates a user's password reset flag for account security management](updates-a-users-password-reset-flag-for-account-security-management.md). Omitting it reads as `false` and clears any pending forced reset, so resend the current value if you are not deliberately changing it. |
| `roleId` | string | No | The permission role to grant, as a 24-character hexadecimal string. Validated only on ObjectId-role tenants, where a value of the wrong length fails with `400` and `The role: {roleId} is invalid. It must be exactly 24 characters long`, and a well-formed value matching no role fails with `404` and `The role for ID: {roleId} could not be found`. Sending an empty string `""` removes the user's role; omitting the field or sending `null` leaves the current role unchanged — the two are not equivalent. On numeric-role tenants this endpoint does not change the user's role. |

## Sample request

```bash title="cURL"
curl -X PUT '{base_url}/api/users/{username}' \
  -H 'Authorization: Bearer {accessToken}' \
  -H 'X-Use-SSO: true' \
  -H 'Content-Type: application/json' \
  -d '{
  "username": "string",
  "firstName": "string",
  "lastName": "string",
  "displayName": "string",
  "email": "member@example.com",
  "cardId": "string",
  "siteGroupId": "string",
  "preferredSiteId": "string",
  "avatarId": "string",
  "isActive": true,
  "isLocked": true,
  "isPasswordResetFlagSet": true,
  "roleId": "string"
}'
```

## Errors

| Status | Meaning | When |
|--------|---------|------|
| `400` | Bad request | `username` in the body does not match the one in the path: `The username in the request: {username} and the one in route: {username} do not match`. Checked before every other rule. |
| `400` | Bad request | A required field is blank, or a field exceeds its maximum length — for example `Username cannot be more than 8 characters`. |
| `400` | Bad request | `email` is non-empty but not a valid address: `The email in the request: {email} is invalid`. |
| `400` | Bad request | `preferredSiteId` is not one of the sites in `siteGroupId`: `The preferred site Id: {preferredSiteId} is not contained in the given site group`. |
| `400` | Bad request | On ObjectId-role tenants only, `roleId` is not exactly 24 characters: `The role: {roleId} is invalid. It must be exactly 24 characters long`. |
| `404` | Not found | `siteGroupId` does not resolve to a site group: `The site group in the request: {siteGroupId} could not be found`. This is a request-data problem returned as a `404`, not a `400`. |
| `404` | Not found | On ObjectId-role tenants only, `roleId` is well-formed but matches no role: `The role for ID: {roleId} could not be found`. |
| `404` | Not found | No account exists with that username: `Failed to find the user with username: {username} in SQL database`, or `User {username} cannot be found when calling Update` on ObjectId-role tenants, where the account must be fully set up. |
| `500` | Internal server error | Any unexpected server-side failure, including a conflict raised when a value on the account cannot be changed — that case is not translated into a `409` by this endpoint and surfaces as a bodyless `500`. Also returned when the two halves of the update fail to complete together. |
