How a mobile app talks to the Yama chat API (v1), and in which order.
The app authenticates as an end-user with a short-lived JWT. The app never holds tenant credentials:
Authorization: Bearer <jwt>.The token is valid for 24 h. While it is still valid, the app can extend the session itself with POST /api/v1/token/refresh (no backend round-trip). Once it has fully expired, ask your backend to mint a new one. All resources use UUID ids; rooms can also be addressed by their slug.
https://yama.modulotech.fr · Staging: https://staging.yama.modulotech.fr. Always over HTTPS.Your backend authenticates as the tenant (its X-Api-Key + X-Api-Secret — never shipped in the app) and mints a JWT for one of its users, identified by the external_id your backend assigned:
# server-side only — provision the user once (idempotent on external_id) curl -X POST https://yama.modulotech.fr/api/v1/users \ -H "X-Api-Key: $API_KEY" -H "X-Api-Secret: $API_SECRET" \ -H "Content-Type: application/json" \ -d '{"user":{"external_id":"ext-alice","first_name":"Alice"}}' # server-side only — mint a token for that user (valid 24h) curl -X POST https://yama.modulotech.fr/api/v1/users/ext-alice/token \ -H "X-Api-Key: $API_KEY" -H "X-Api-Secret: $API_SECRET" # 201 Created { "token": "eyJhbGciOiJIUzI1Ni…", "user": { "id": "uuid", "external_id": "ext-alice", "display_name": "Alice" } }
The app receives token and uses it as the bearer below. It never sees X-Api-Key/X-Api-Secret. Re-mint when the token expires.
Or — direct password login. If the user has a password (set from the admin), the app can log them in itself, without your backend. The tenant slug is in the URL (users are only unique within a tenant):
curl -X POST https://yama.modulotech.fr/api/v1/tenants/<tenant-slug>/login \
-H "Content-Type: application/json" \
-d '{"login":{"email":"alice@example.com","password":"••••••"}}'
# 201 Created
{ "token": "eyJhbGciOiJIUzI1Ni…", "user": { "id": "uuid", "external_id": "ext-alice", "display_name": "Alice" } }
curl https://yama.modulotech.fr/api/v1/me -H "Authorization: Bearer $JWT"
curl -X PATCH https://yama.modulotech.fr/api/v1/me \
-H "Authorization: Bearer $JWT" -H "Content-Type: application/json" \
-d '{"status":"connected"}'
mine to discover all rooms of the tenant). Each room carries member, and — for rooms the user belongs to — unread_count (the current user's unread messages; null for non-member rooms).
?unread=true, ?from= / ?to= (ISO8601) for incremental sync. Each message has reads_count, read_by_me, and a nested user object (the author: id, external_id, display_name, first_name, last_name, status) alongside the raw user_id.
curl "https://yama.modulotech.fr/api/v1/rooms/support/messages?from=2026-06-18T00:00:00Z" \ -H "Authorization: Bearer $JWT"
# text curl -X POST https://yama.modulotech.fr/api/v1/rooms/support/messages \ -H "Authorization: Bearer $JWT" -H "Content-Type: application/json" \ -d '{"message":{"content":"Hello 👋"}}' # with a file (multipart) curl -X POST https://yama.modulotech.fr/api/v1/rooms/support/messages \ -H "Authorization: Bearer $JWT" \ -F "message[content]=See attached" -F "message[file]=@/path/photo.jpg"
Attachments: photos, videos or PDF only (incl. Apple HEIC/HEIF and QuickTime/MOV), max 75 MB. Anything else returns 422.
marked_count. Undo a single one with DELETE /api/v1/messages/{id}/read.
user object), plus reads: who read it and when, and file if any.
{"status":"disconnected"}.
| Method | Path | Purpose |
|---|---|---|
| POST | /api/v1/tenants/{tenant_slug}/login | Direct login (email + password) → JWT |
| GET | /api/v1/me | Current user profile |
| PATCH | /api/v1/me | Update status (connected/disconnected/unknown) |
| GET | /api/v1/rooms | List rooms (?mine=true) |
| GET | /api/v1/rooms/{slug} | Room detail (slug or id) |
| POST | /api/v1/rooms/{slug}/membership | Join |
| DELETE | /api/v1/rooms/{slug}/membership | Leave |
| GET | /api/v1/rooms/{slug}/messages | List messages (unread, from, to) |
| POST | /api/v1/rooms/{slug}/messages | Send a message (text and/or file) |
| GET | /api/v1/messages/{id} | Message detail + read receipts |
| POST | /api/v1/messages/{id}/read | Mark read |
| DELETE | /api/v1/messages/{id}/read | Mark unread |
| POST | /api/v1/token/refresh | Exchange a valid JWT for a fresh one |
| POST | /api/v1/logout | Revoke current tokens + mark disconnected |
| Status | Meaning | What the app should do |
|---|---|---|
401 | Missing / invalid / expired token | Request a fresh JWT from your backend, retry once |
403 | Not a member of the room | Join the room first, or hide the action |
404 | Unknown room / message | Refresh the list; the resource may be gone |
422 | Invalid payload (e.g. empty message, bad status) | Show a validation error; check errors |