WhatsApp Gateway
API referenceChats

Mark a chat as read

Clears the unread counter on the chat in the path (`cid`) and returns the updated chat. This updates the **gateway's own unread state** — the value the chat viewer reads — and does **not** send per-message read receipts to WhatsApp. **Idempotent:** calling it again on an already-read chat is a no-op and returns the same chat. Requires the `send` capability. **Errors** - `not_found` (404): the session does not exist, is not owned by the caller's organization, or no chat with that `cid` is stored. - `forbidden` (403): the caller lacks the `send` capability.

POST
/api/v1/sessions/{session}/chats/{cid}/read

Authorization

AuthorizationBearer <token>

Send Authorization: Bearer <token>. The router accepts two kinds of token and tries each in turn: a frontend-minted login JWT (verified against the frontend JWKS; the person's org + role are read from it), or an api-key for a script/service (carrying a fixed set of gateway permissions). The bearerFormat: JWT label describes the person-login case.

In: header

Path Parameters

session*string

The WhatsApp session id (a session is one attached WhatsApp number). The session must be owned by the caller's organization or the request fails with not_found.

cid*string

The chat's JID to mark as read. Format is 123...@s.whatsapp.net for a direct chat or 123...@g.us for a group.

Response Body

application/json

application/json

curl -X POST "https://example.com/api/v1/sessions/01HF.../chats/6281234567890@s.whatsapp.net/read"
{  "archived": false,  "id": 3344,  "jid": "6281234567890@s.whatsapp.net",  "lastMessageAt": 1719662400000,  "mutedUntil": 1751198400000,  "name": "Alice",  "pinned": false,  "sessionId": "01J9ZX8K2QHV0M3T6R7P4N5W8C",  "type": "dm",  "unreadCount": 3}
{  "error": {    "code": "not_found",    "details": {      "property1": null,      "property2": null    },    "message": "session not found"  }}

Set chat presence (typing/recording) PUT

Broadcasts a typing/recording presence indicator to the chat in the path (`cid`). Set `state` to `composing` ("typing…"), `recording` ("recording audio…"), or `paused` (clear the indicator). **Live client required:** this goes straight to WhatsApp and needs a live, connected client for the session. If the session has no connected client, the call returns **501 `not_implemented`**. On success returns **204 No Content** with no body. The indicator is transient — WhatsApp clears it after a short timeout, so re-send periodically to keep it showing. Requires the `send` capability. **Errors** - `validation_error` (400/422): `state` is missing or not one of the allowed values. - `not_found` (404): the session does not exist or is not owned by the caller's organization. - `not_implemented` (501): the session has no connected WhatsApp client to deliver the presence. - `forbidden` (403): the caller lacks the `send` capability.

List a session's contacts (found users) GET

Returns the people this session has seen, one page at a time. Served entirely from the gateway's **stored** WhatsApp data, so it works even when the session is currently disconnected — no live WhatsApp connection is required. **Filters** (all optional, combinable): - `source=dm` — keep only people you have a direct chat with. - `source=group` — keep only people seen in a group. - `group={jid}` — keep only members of the given group JID. - `q=` — case-insensitive substring search over each contact's name or number. **Pagination:** results are cursor-paged. Use `limit` (1–200, default 50) to size the page and pass the response's `nextCursor` back as `cursor` to fetch the next page. An empty `nextCursor` means there are no more pages. **Auth:** requires the `read` capability. **Errors:** `404` (`not_found`) if the session does not exist or is not owned by the caller's organization; `400` (`validation_error`) if a query parameter is malformed.