# Users

Users are your site's users, addressed by your own id: the `sub` of the client token, called `external_id` in the API. mondochat stores a display name, avatar, language and role, never an email.

## Before you start

- An app with an active server secret. Every call below sends two headers:

  ```text
  Authorization: Bearer <mcs_ server secret>
  Mondochat-App-Id: <app id>
  ```

- Roles that exist in the app's settings, if you pass `role`. See [Settings overview](/settings/overview/).
- For export and delete, a webhook endpoint that listens for `user.exported` and `user.deleted`. See [Webhooks overview](/webhooks/overview/).

## Steps

1. Create or update a user without a browser, for example so others can open a conversation with them before they ever sign in. `name` is required. `avatar` (https), `role`, `lang`, `perms` and `tier` are optional.

   ```sh
   curl -X PUT https://app.mondochat.io/v1/server/users/bob \
     -H "Authorization: Bearer $MONDOCHAT_SERVER_SECRET" -H "Mondochat-App-Id: $MONDOCHAT_APP_ID" \
     -H 'content-type: application/json' \
     -d '{"name": "Bob", "role": "member", "lang": "en"}'
   ```

   The answer is `{id, external_id, kind, role, name, avatar}`. `kind` is `member` or `guest`. A user also exists after their first session. An upsert alone sends no `user.created` webhook: it goes out with the user's first session, whether from an exchange, `POST /v1/server/sessions` or an installed-app sign-in. An avatar the user uploaded wins over `avatar`, and an upsert never clears it. See [Upsert a user](/api/operations/upsert-user/).

2. Read a user back with [`GET /v1/server/users/{external_id}`](/api/operations/get-user/):

   ```sh
   curl https://app.mondochat.io/v1/server/users/bob \
     -H "Authorization: Bearer $MONDOCHAT_SERVER_SECRET" -H "Mondochat-App-Id: $MONDOCHAT_APP_ID"
   ```

3. Export a user's data. The call answers 202 with `{export_id}`. When the export is ready, a `user.exported` webhook carries a download URL that works for 24 hours. The export is a zip in S3 with a JSON file (profile, conversations, messages, purchases and reports by the user) and the user's photos.

   ```sh
   curl -X POST https://app.mondochat.io/v1/server/users/bob/export \
     -H "Authorization: Bearer $MONDOCHAT_SERVER_SECRET" -H "Mondochat-App-Id: $MONDOCHAT_APP_ID" \
     -H 'Idempotency-Key: export-bob-1'
   ```

   See [Export a user](/api/operations/export-user/).

4. Delete a user. The call answers 202. The user and their data are deleted within 24 hours, then a `user.deleted` webhook is sent.

   ```sh
   curl -X DELETE https://app.mondochat.io/v1/server/users/bob \
     -H "Authorization: Bearer $MONDOCHAT_SERVER_SECRET" -H "Mondochat-App-Id: $MONDOCHAT_APP_ID"
   ```

   Deletion removes the user's messages, photos, IPs and profile fields. The row stays as a tombstone so conversations keep their shape: the peer sees "deleted user" and only their own messages. The open frame gets `session.revoked` with reason `deleted`, and the user's devices are deleted. See [Delete a user](/api/operations/delete-user/).

5. Revoke a user's installed-app devices when they lose chat access. The call answers 204, or 404 for an unknown user:

   ```sh
   curl -X DELETE https://app.mondochat.io/v1/server/users/bob/devices \
     -H "Authorization: Bearer $MONDOCHAT_SERVER_SECRET" -H "Mondochat-App-Id: $MONDOCHAT_APP_ID"
   ```

   Their sessions stop refreshing within 15 minutes. See [Installed app](/guides/installed-app/) and [Revoke devices](/api/operations/revoke-devices/).

6. Let users manage their own profile from the chat or a headless client. With a session token, [`GET /v1/me`](/api/operations/get-me/) returns the user and [`PATCH /v1/me`](/api/operations/patch-me/) changes these fields. A field left out is unchanged, and any other value answers 422.

   | Field | Values | Effect |
   |---|---|---|
   | `lang` | `en`, `it`, `es`, `fr` | The user's interface language. |
   | `last_seen` | `everyone` or `nobody` | `nobody` hides the user's last seen from everyone and everyone's from them. `online` stays visible. |
   | `read_receipts` | `true` or `false` | `false` stops read ticks both ways. |
   | `inbound` | `everyone` or `members` | `members` refuses guests. It needs `workspace.inbound` and a member that is not guest-like, else 403 `policy_denied` with reason `inbound`. |

   New users have `last_seen: everyone`, `read_receipts: true` and `inbound: everyone`. Webhooks carry the real `last_seen_at` and read pointer whatever these choices say, because they go to your server and not to peers. With `chat.avatar_upload` on, a user uploads a picture that photo moderation checks before it shows.

## Check it works

- The upsert answers 200 with the user, and `GET` returns the same `external_id`.
- After step 3, your endpoint receives `user.exported` with a `url` and `expires_at`. After step 4, it receives `user.deleted` with the `external_id`.
- After step 4, the peer's inbox shows "deleted user".

## When it fails

| Answer | Cause |
|---|---|
| 401 `unauthorized` | The secret or the app id is missing, wrong or revoked. |
| 404 `not_found` | The user does not exist in this app. Ids from another app answer 404 too. |
| 409 `idempotency_conflict` | The same `Idempotency-Key` was sent with a different body. |
| 422 `validation` | A field is missing or malformed, for example a `role` that is not in the app's settings. `errors` lists each one. |
| 403 `forbidden` with reason `test_user_limit` | A tenant in `test` status accepts 50 users per app. |

## Limits

- Export and delete take an `Idempotency-Key`. A repeat with the same key within 24 hours returns the first answer with `Idempotent-Replayed: true`.
- A deletion finishes within 24 hours, not at once.
- An export URL works for 24 hours.
- After a hard delete, a client that holds an old sync cursor for an affected conversation gets 410 `gone` with reason `stale_cursor` and reloads. See [Sync and recovery](/realtime/sync/).
- `retention.messages_days`, `retention.photos_days` and `retention.ip_log_days` (default 90) delete old data on their own. See [Settings reference](/reference/settings/).

## Related topics

- [Upsert a user](/api/operations/upsert-user/)
- [Export a user](/api/operations/export-user/)
- [Delete a user](/api/operations/delete-user/)
- [Settings reference](/reference/settings/)
- [Errors](/errors/)
- [Sessions and tokens](/guides/sessions-and-tokens/)