Read states¶
A read state records how far the current account has read in one channel. It holds the highest message read through, the mention count, and the last acknowledged pin timestamp. The Messages resource defines single-message acknowledgement, read state deletion, and pin acknowledgement.
All routes on this page are user-only. They require a user session token (Authorization: Bearer <token>); a valid Bot <token> credential is rejected with 403 ACCESS_DENIED (an invalid one with 401 UNAUTHORIZED).
Read state object¶
An account holds at most one read state per channel, keyed by the account and the channel.
Structure¶
| Field | Type | Description |
|---|---|---|
| id | snowflake | The ID of the channel the read state belongs to |
| mention_count | integer | The number of unread mentions stored for the channel |
| last_message_id | ?snowflake | The highest message ID the account has read through, or null when the entry stores no watermark |
| last_pin_timestamp | ?ISO8601 timestamp | The time of the last acknowledged pin, or null when pins have never been acknowledged |
| version | string | The read state version as a decimal unsigned 64-bit string. Always "0" |
mention_count is the value the last acknowledgement stored. The server increments it as new mentions arrive and never recomputes it from message history.
An acknowledgement creates an entry. So does Acknowledge pins, and so does the server when a mention arrives in a channel the account has no entry for. A pin acknowledgement writes only last_pin_timestamp. The entry the server creates for a mention starts from the channel's own baseline watermark (the channel ID itself), so every message already in the channel stays unread.
Clear channel read state deletes the whole entry, dropping the watermark and the mention count along with the pin timestamp.
The aggregate is delivered through the Gateway
The complete entry array arrives as read_states in the Gateway Ready payload. TEBHOS also exposes it over HTTP through List read states.
Only an acknowledgement produces a later Dispatch. A message acknowledgement emits Message ACK and a pin acknowledgement emits Channel Pins ACK, both to the caller's own sessions. A server-side mention increment and Clear channel read state change the stored entry and emit nothing, so the Dispatch stream is not a complete change feed. A client that needs the authoritative aggregate reconciles from a new Ready, from List read states, or from the entries Acknowledge read states returns.
Example¶
{
"id": "1501314428688998182",
"mention_count": 3,
"last_message_id": "1501320000000000000",
"last_pin_timestamp": null,
"version": "0"
}
Read state acknowledgement object¶
One entry in the body of Acknowledge read states. It names the channel whose entry changes, the message the account has read through, and the mention count to store for the channel.
Structure¶
| Field | Type | Description |
|---|---|---|
| channel_id | snowflake | The ID of the channel whose read watermark changes |
| message_id | snowflake | The ID of the message watermark to acknowledge |
| mention_count? | integer | The number of mentions to store for the channel (default 0) |
| manual? | boolean | Whether the stored watermark becomes exactly message_id (default false) |
TEBHOS resolves neither the channel nor the message. The values are stored as the read state key with no check that the channel exists or that the account can see it. The submitted mention_count is stored verbatim, so a client that leaves unread mentions after message_id must send the number of those mentions.
With manual false, an entry strictly below the stored watermark is discarded and the stored entry is returned unchanged. An entry equal to the stored watermark still produces a write, so it resets the stored mention count.
A manual acknowledgement can rewind the watermark
A lower message_id with manual: true marks later messages unread again. The stored mention count is replaced at the same time, so a rewinding acknowledgement that omits mention_count clears it.
Example¶
Bulk read state acknowledgement object¶
One entry in the body of Mark channels as read.
Structure¶
| Field | Type | Description |
|---|---|---|
| channel_id | snowflake | The ID of the channel whose read watermark changes |
| message_id | snowflake | The ID of the message watermark to acknowledge |
Neither identifier is resolved, so an entry may name a channel or a message that does not exist.
List read states¶
GET /users/@me/read-states
Returns every read state the current account holds. User-only.
Response body¶
| Field | Type | Description |
|---|---|---|
| read_states | array[read state object] | Every stored entry for the account |
Acknowledge read states¶
POST /read-states/ack
Applies a list of read state acknowledgement objects to the current account's aggregate and returns the resulting read state entries. User-only. Emits one Message ACK Gateway event to the caller's own sessions for each submitted entry.
The route resolves no channel and evaluates no permission. The write changes only the caller's own read states. Entries are applied one after another in submitted order.
JSON body¶
| Field | Type | Description |
|---|---|---|
| read_states | array[read state acknowledgement object] | The acknowledgements to apply |
A channel_id or message_id that is not a valid snowflake fails the request with 400 BAD_REQUEST. Entries processed before the invalid one stay written.
Response body¶
| Field | Type | Description |
|---|---|---|
| read_states | array[read state object] | The authoritative entries after the write, one per submitted entry, in submitted order. An entry whose watermark did not move reports the values already stored |
| read_state_proto | string | The same entries encoded as a base64 protobuf ReadStateBundle (empty string when there are no entries) |
Response¶
| Status | Body | Condition |
|---|---|---|
| 200 | response body | Acknowledgements were processed and the resulting entries returned |
| 400 | error response | An identifier is not a valid snowflake |
| 401 | error response | Missing or non-user credential |
Side effects¶
With manual true, an entry sets the channel watermark to message_id. With manual false, TEBHOS discards an entry strictly below the stored watermark and leaves the stored entry unchanged. Every entry that is not discarded sets the watermark to message_id and stores the submitted mention count.
One Message ACK goes to the caller's sessions for every submitted entry, including an unchanged one. It carries channel_id, message_id, mention_count, last_pin_timestamp, version and the submitted manual value. Dispatch is best-effort: if it fails, the request still succeeds.
Rate limit¶
20 requests per 10 seconds for each authenticated user, on the read_state:ack_bulk bucket, shared with Mark channels as read.
Mark channels as read¶
POST /read-states/ack-bulk
Applies a list of bulk read state acknowledgement objects and returns 204 with an empty body. User-only. Emits one Message ACK Gateway event to the caller's own sessions for each submitted entry.
Every entry advances its channel watermark monotonically and sets the channel's stored mention count to 0. An entry whose message_id is strictly below the stored watermark is skipped and leaves the stored entry untouched. Like Acknowledge read states, the operation resolves no channel and evaluates no permission.
JSON body¶
| Field | Type | Description |
|---|---|---|
| read_states | array[bulk read state acknowledgement object] | The channel and message pairs to acknowledge |
Two entries naming the same channel are both evaluated against the value stored before the request.
Response¶
| Status | Body | Condition |
|---|---|---|
| 204 | empty | Acknowledgements were processed |
| 400 | error response | An identifier is not a valid snowflake (nothing is written) |
| 401 | error response | Missing or non-user credential |
Side effects¶
An entry equal to the current watermark resets the mention count to 0. One Message ACK with manual: false goes to the caller's own sessions for every submitted entry; a skipped entry reports the stored watermark and mention count.
Retries are safe
Writes are applied in batches. A request that fails partway leaves the batches already written in place. A retry is safe, because a repeated monotonic acknowledgement is idempotent.
Rate limit¶
20 requests per 10 seconds for each authenticated user, on the read_state:ack_bulk bucket, shared with Acknowledge read states.