---
title: "Batch update users (chat API) (v2, tenant actor)"
method: PATCH
path: "/v2/apps/{appId}/chats/users"
tags: ["Chats"]
---

# Batch update users (chat API) (v2, tenant actor)

`PATCH /v2/apps/{appId}/chats/users`

## Request

- Base URL: `https://api.chat.ethora.com`
- URL: `https://api.chat.ethora.com/v2/apps/{appId}/chats/users`
- Auth: one of:
  - HTTP bearer
  - HTTP bearer

**Auth**: Tenant Actor

Same operation as `/v2/chats/users` but with the target app explicit
in the path. This is the preferred route for backend / tenant-admin
integrations, especially when:

- You're acting on a CHILD app from a parent-app B2B token. The
  legacy `/v2/chats/users` route uses `reqUser.appId` for the
  xmppUsername-prefix check and DB scope, so a parent-app token can
  never satisfy `xmppUsername.startsWith(<childAppId>_)` for a child
  app's user. This route uses the path `:appId` instead.

- You want the target appId to be unambiguous in the URL for
  ops/audit/logging purposes (matches `/v2/apps/{appId}/chats/broadcast`
  and the rest of the path-explicit tenant-actor surface).

Auth model:
- `B2B Server JWT`: allowed when `:appId` is the token's own app or
  one of its child apps (parentAppId equality).
- `User JWT`: allowed when the caller has an `appUsers.update` Acl
  on `:appId`.

Request body shape and response envelope are identical to
`/v2/chats/users` (same Joi schema, same patchChatUsersBatchService
under the hood; only the auth + the `appId` resolution differ).

## Path parameters

- `appId` string, required

## Request body

- object
  - `users` object[], required: Array of user-record patches. Each item must include `uuid` OR `xmppUsername` to identify the target user; any remaining keys (`firstName`, `lastName`, `username`, `profileImage`, `email`) are applied. The endpoint returns per-item status under `results[]` so a single bad row never fails the whole batch.
    - `uuid` string
    - `xmppUsername` string
    - `firstName` string
    - `lastName` string
    - `username` string
    - `profileImage` string: URL
    - `email` string, email: Validated against the deployment's EMAIL_TLD_VALIDATION policy (off | icann | list); trim/lowercase normalized server-side. Pass an empty string to clear the email. Returns `status: 'invalid'` if the new value is already used by another user in the same appId.

## Response `200`

```
{ ok: true, summary, results, details }
```
Per-user statuses match `/v2/chats/users` PATCH (`updated` /
`not-found` / `skipped` / `invalid`); inspect `details[].status`
per item to know if a row actually changed.

## Other responses

- `400`: Bad Request
- `401`: Unauthorized
- `403`: Cross-tenant write denied (token cannot manage :appId).
- `404`: Not Found
- `422`: Unprocessable Entity
- `500`: Internal Server Error

---

[API](https://skmtc.dev/ethora/apis/platform-api.md) · [All operations](https://skmtc.dev/ethora/apis/platform-api/llms.txt) · [OpenAPI document](https://skmtc.dev/ethora/apis/platform-api/revisions/01b6e64378ee?raw)
