> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kodelabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Manage users

> List the users of your organization, create or invite a user, give and change app access, activate or deactivate a user, and remove access or delete a user through the KODE API.

Use the users operations to manage who can sign in to your organization and what they can open. A user has an organization role and, in KODE OS, a KODE OS role. You set the organization role when you create, invite, or update the user. You give app access in a separate step.

Each linked endpoint can switch the sample between cURL, JavaScript, Python, and PowerShell, or send the request for you.

> [!WARNING]
>
> Some of these requests send email to the person you add, and a delete cannot be undone. Test with an address you own, and on a non-production environment first.

## Before you begin

You need:

* An access token. See the [quickstart](https://docs.kodelabs.com/products/kode-api/quickstart.md).
* A service account whose role allows the user actions you call: list, get, create, invite, update, delete, assign app, update KODE OS role, and unassign app. Each operation needs its own action.
* For app access, the `buildingId` of each building the user can open. See [List buildings](https://docs.kodelabs.com/products/kode-api/api-reference/buildings/list-buildings.md).

You see and change only the users of your own organization. A `userId` from another organization returns `404`.

## How users and app access fit together

| Field | Meaning |
| - | - |
| `role` | The organization role, for example `CLIENT_ADMIN` or `CLIENT_RESTRICTED_ACCESS`. |
| `memberType` | `INTERNAL` for a user you create. `GUEST` for a user you invite from another organization. |
| `active` | Whether the account is enabled. You set it when you create the user, and you can change it later. |
| `invitationStatus` | `PENDING` or `ACCEPTED` for a guest. Absent for an internal user. |
| `assignedInApps` | The apps the user can access. Each entry has `appId`, the app `role`, and `assignmentStatus`. |

A new user has no app access. The organization role and the KODE OS role are separate, and each has its own operation to change it.

## List users

Call [List users](https://docs.kodelabs.com/products/kode-api/api-reference/users/list-users.md). Each item in `data` is a user, with the apps the user can access in `assignedInApps`.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl "https://api.kodelabs.com/kodeos/api/v1/users?limit=50&page=1" \
  --header "Authorization: Bearer ${KODE_ACCESS_TOKEN}"
```

Add filters to narrow the list. A user must match every filter you send.

| Parameter | Use |
| - | - |
| `q` | Search by email address. The match is not case-sensitive. |
| `role` | Organization role. Repeat it to match any of several roles. |
| `active` | `true` for active users, `false` for inactive users. |
| `appId` | Users who have this app, for example `bms`. Repeat it to match any of several apps. |
| `memberType` | `INTERNAL` or `GUEST`. The value is case-sensitive. |

Users come back by `_id`, highest first. Use `asc` or `dsc` to sort by another field, such as `email`. Stop requesting pages when `page` equals `pageCount`. See [pagination](https://docs.kodelabs.com/products/kode-api/fundamentals.md#pagination).

Keep the `_id` of the user you want. Every other operation takes it as `userId`.

## Create or invite a user

Choose the operation by where the person belongs.

| | [Create a user](https://docs.kodelabs.com/products/kode-api/api-reference/users/create-a-user.md) | [Invite a user](https://docs.kodelabs.com/products/kode-api/api-reference/users/invite-a-user.md) |
| - | - | - |
| For | A person who belongs to your organization. | A person from another organization. |
| `memberType` | `INTERNAL` | `GUEST` |
| You send | `firstName`, `lastName`, `email`, `role`, `active`. Optionally `phone` and `position`. | `trustedOrganizationId`, `email`, `role`. Optionally `position`. |
| `active` | You choose `true` or `false`. | Always `true`. |
| Email | Sent when `active` is `true`. | Sent only when the invitee already has an account in the organization you name. |

### Create a user

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --request POST \
  "https://api.kodelabs.com/kodeos/api/v1/users" \
  --header "Authorization: Bearer ${KODE_ACCESS_TOKEN}" \
  --header "Content-Type: application/json" \
  --data '{
    "firstName": "Jordan",
    "lastName": "Lee",
    "email": "jordan.lee@example.com",
    "role": "CLIENT_RESTRICTED_ACCESS",
    "active": false,
    "phone": "+14155552671",
    "position": "Facilities coordinator"
  }'
```

The response is `200` and holds the new user, with its `_id`.

> [!WARNING]
>
> With `active` set to `true`, KODE emails the user a link to set a password. With `active` set to `false`, the account is created disabled and no email is sent. To enable the account later, [update the status of the user](#activate-or-deactivate-a-user).

Keep these rules in mind:

* The email address must be unique in your organization, in any letter case. KODE stores it in lower case. A duplicate returns `400`.
* `firstName` and `lastName` can have up to 40 characters, and `position` up to 200.
* `phone` must be an international number that starts with `+`.
* Values that contain HTML tags are stored HTML-escaped.

### Invite a guest

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --request POST \
  "https://api.kodelabs.com/kodeos/api/v1/users/invite" \
  --header "Authorization: Bearer ${KODE_ACCESS_TOKEN}" \
  --header "Content-Type: application/json" \
  --data '{
    "trustedOrganizationId": "67c95f0aa1b2c3d4e5f60718",
    "email": "guest@example.com",
    "role": "CLIENT_RESTRICTED_ACCESS",
    "position": "Contractor"
  }'
```

The response is `200` and holds the guest, with `memberType` set to `GUEST`, `active` set to `true`, and `invitationStatus` set to `PENDING`. The guest has no name. If `trustedOrganizationId` does not match an organization, or the email already belongs to a user, the request fails with `400`.

## Give app access to a user

App access is asynchronous. You send the assignment, KODE finishes it in the background, and you read the user to see the result. Only the `bms` app can be assigned through the API.

**1. Assign the app**

Call [Assign an app to a user](https://docs.kodelabs.com/products/kode-api/api-reference/users/assign-an-app-to-a-user.md) with `bms` as the `appId`. Send the KODE OS role in `role`. Use a role of your organization's `bms` app, for example `ViewOnly`. To limit access to some buildings, send their IDs in `buildingIds`.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --request POST \
  "https://api.kodelabs.com/kodeos/api/v1/users/${USER_ID}/apps/bms" \
  --header "Authorization: Bearer ${KODE_ACCESS_TOKEN}" \
  --header "Content-Type: application/json" \
  --data '{
    "role": "ViewOnly",
    "buildingIds": ["67c960faffaa42223163099e"]
  }'
```

The response is `202` with no body. That means KODE accepted the request, not that access is ready. A user who already has the app gets `400`.

**2. Wait until the assignment finishes**

Call [Get a user](https://docs.kodelabs.com/products/kode-api/api-reference/users/get-a-user.md) until the `bms` entry in `assignedInApps` reaches a final status.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl "https://api.kodelabs.com/kodeos/api/v1/users/${USER_ID}" \
  --header "Authorization: Bearer ${KODE_ACCESS_TOKEN}"
```

Read `assignedInApps[].assignmentStatus`:

| Status | Meaning |
| - | - |
| `INITIALIZING` | KODE accepted the assignment and has not started it. |
| `PROCESSING` | KODE is setting up the access. |
| `SUCCEEDED` | The user has access to the app. |
| `FAILED` | The assignment did not finish. |
| `PENDING` | The user is a guest who has not accepted the invitation. Nothing runs until the guest accepts. |

Poll every few seconds and stop on `SUCCEEDED` or `FAILED`.

> [!NOTE]
>
> The buildings you send in `buildingIds` are not returned by the API. `assignedInApps` lists the app, the role, and the status only.

> [!WARNING]
>
> Assigning an app to an active user can send that user an email.

## Change a role

A user has two roles. Use the operation that matches the one you want to change.

| To change | Call |
| - | - |
| The organization role, `role` on the user | [Update a user](https://docs.kodelabs.com/products/kode-api/api-reference/users/update-a-user.md) |
| The role in an app, `assignedInApps[].role` | [Update the KODE OS role of a user](https://docs.kodelabs.com/products/kode-api/api-reference/users/update-the-kode-os-role-of-a-user.md) |

### Update a user

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --request PUT \
  "https://api.kodelabs.com/kodeos/api/v1/users/${USER_ID}" \
  --header "Authorization: Bearer ${KODE_ACCESS_TOKEN}" \
  --header "Content-Type: application/json" \
  --data '{
    "firstName": "Jordan",
    "position": "Facilities lead"
  }'
```

This call changes `firstName`, `lastName`, `phone`, `position`, and the organization `role`. You cannot change `email`. To change `active`, [update the status of the user](#activate-or-deactivate-a-user). Setting `role` to `CLIENT_ADMIN` returns `403`.

### Update the KODE OS role

The user must already have the app, and the assignment must be finished. Wait for `SUCCEEDED` or `FAILED` first.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --request PUT \
  "https://api.kodelabs.com/kodeos/api/v1/users/${USER_ID}/apps/bms" \
  --header "Authorization: Bearer ${KODE_ACCESS_TOKEN}" \
  --header "Content-Type: application/json" \
  --data '{ "role": "Engineering" }'
```

The response is `204` with no body. This changes the role in the app only, not the organization role. If the user does not have the app, the request fails with `400` and the message "App not found".

## Remove app access

Call [Remove app access from a user](https://docs.kodelabs.com/products/kode-api/api-reference/users/remove-app-access-from-a-user.md). The user stays in your organization.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --request DELETE \
  "https://api.kodelabs.com/kodeos/api/v1/users/${USER_ID}/apps/bms" \
  --header "Authorization: Bearer ${KODE_ACCESS_TOKEN}"
```

The response is `204` with no body. Read the user to confirm that the `bms` entry is gone from `assignedInApps`. If the user does not have the app, the request fails with `400` and the message "App not found". You can remove access that is still `PENDING`.

## Activate or deactivate a user

Call [Update the status of a user](https://docs.kodelabs.com/products/kode-api/api-reference/users/update-the-status-of-a-user.md) with `active` set to `true` or `false`. The user stays in your organization.

> [!WARNING]
>
> The first time KODE activates a user, it sends an account email with a link to set a password. Check the email address before you send the request.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --request PUT \
  "https://api.kodelabs.com/kodeos/api/v1/users/${USER_ID}/status" \
  --header "Authorization: Bearer ${KODE_ACCESS_TOKEN}" \
  --header "Content-Type: application/json" \
  --data '{ "active": false }'
```

The response is `200` and holds the updated user. If the user already has the status you sent, the request fails with `400` and the message "Invalid status update". To take away app access and keep the account enabled, remove the app access as in the previous section.

## Delete a user

To remove the user, call [Delete a user](https://docs.kodelabs.com/products/kode-api/api-reference/users/delete-a-user.md).

> [!WARNING]
>
> A delete is permanent and cannot be undone. The user and the user's app access are removed. The delete succeeds even while an app assignment is still being set up.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --request DELETE \
  "https://api.kodelabs.com/kodeos/api/v1/users/${USER_ID}" \
  --header "Authorization: Bearer ${KODE_ACCESS_TOKEN}"
```

The response is `204` with no body. Read the user afterwards to confirm: it returns `404`. A second delete of the same user also returns `404`. No email is sent. If you later create a user with the same email address, the new user gets the same `_id`.

## Handle errors

Error responses hold an `error` object with a `code`, a `message`, and, for field checks, `fields`.

| Status | When | What to do |
| - | - | - |
| `400` | A field is missing, too long, or invalid. The email is already in use. The app is already assigned, or the user does not have it. The `appId` is not `bms`. The user already has the status you sent. | Read `error.message` and `error.fields`, then correct the request. |
| `403` | The service account is not allowed to do this, or to grant this role. | Check the service account's role. |
| `404` | The `userId` does not exist in your organization. | List the users again and use a current `_id`. |

After a timeout or a `5xx` on a create or invite, search the list with `q` before you send the request again, because the user can already exist. See [retry failed requests](https://docs.kodelabs.com/products/kode-api/fundamentals.md#retry-failed-requests).

## Limits

| Field | Limit |
| - | - |
| `firstName`, `lastName` | 40 characters |
| `position` | 200 characters |
| `phone` | International number that starts with `+` |
| `limit` on the list | 1 to 200, default 50 |
| Apps you can assign | `bms` only |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.