Read State
Initially, read states in Discord were built to keep track of unread messages and pings in channels. Over time, the system evolved and now powers the unread and badging system across many other surfaces, as indicated by read state type.
How Unreads Work
Read states are a simple data store that contain the last acknowledged entity ID (message, guild scheduled event, etc.). As snowflakes are monotonically increasing, any entity with an ID greater than the last acknowledged ID is considered unread. A resource is considered unread if there exists at least one entity with an ID greater than the last acknowledged ID. For example, a channel is considered unread if there exists at least one message with an ID greater than the last acknowledged message ID.
How this is determined depends on the read state type:
- For read states of type
CHANNEL, the channellast_message_idfield is compared against the read state'slast_message_id. The channellast_pin_timestampfield is also compared against the read state'slast_pin_timestampto determine if there are any unacknowledged pinned messages. - For read states of type
GUILD_EVENT, the ID of the newest scheduled event in the guild is compared against the read state'slast_acked_id. - For read states of type
GUILD_HOME, no specific entity is tracked; guild home is considered unread if the timestamp of the read state'slast_acked_idis more than 24 hours old. - For read states of type
GUILD_ONBOARDING_QUESTION, the guildlatest_onboarding_question_idfield is compared against the read state'slast_acked_id. - For read states of type
NOTIFICATION_CENTER, the ID of the newest notification center item is compared against the read state'slast_acked_id. - For read states of type
MESSAGE_REQUESTS, the ID of the newest message request is compared against the read state'slast_acked_id.
These same principles can be applied to determine what entity ID to use when acknowledging a read state.
Automations
As with all stateful resources in Discord, clients are expected to manage read states locally and update them as necessary from Gateway events such as Message Ack, Channel Pins Ack, Guild Feature Ack, and User Non Channel Ack.
However, certain read state updates are done automatically by Discord without firing ack events. As a result, clients must make sure to keep their local read state store in sync by implementing the same logic.
Channel Read State Automations
- When a Message Create Gateway event is received, if the author is not blocked or ignored:
- If the message is in a private channel, the message type is not
RECIPIENT_REMOVE, and the private channel is not muted or the message mentions the current user, the corresponding read state must be updated to incrementmention_countby one. 1 - If the message is in a guild channel and the message mentions the current user, the corresponding read state must be updated to increment
mention_countby one. 1 - If the message is not in a voice or stage channel, the channel is not muted, the channel's notification level is set to
ALL_MESSAGES, and the user has theMENTION_ON_ALL_MESSAGESsetting enabled, the corresponding read state must be updated to incrementmention_countby one. 2
- If the message is in a private channel, the message type is not
- When a Message Create Gateway event is received, if the message type is not
POLL_RESULTand is authored by the current user, the corresponding read state must be updated to setlast_message_idto the new message's ID, thereby resettingmention_countto zero. - When a Thread Create Gateway event is received, if the thread's parent is a thread-only channel and the thread is created by the current user, the corresponding parent read state must be updated to set
last_message_idto the new thread's ID, thereby resettingmention_countto zero.
1 When considering if a message mentions the current user, clients must take into account user mentions, role mentions, and everyone mentions. When considering role and everyone mentions, clients must check the channel's notification settings to ensure those mentions are not suppressed.
2 These mentions are considered low importance as they do not ping the user. See the Calculating Flags section for more information.
Guild Read State Automations
- When a Guild Scheduled Event Create Gateway event is received, if the guild's notification settings does not have
mute_scheduled_eventsset, the corresponding read state must be updated to incrementbadge_countby one. - When a Guild Scheduled Event Create Gateway event is received, if the event's
creator_idmatches the current user's ID, the corresponding read state must be updated to setlast_acked_idto the new event's ID, thereby resettingbadge_countto zero.
Management
Read states are almost entirely managed by clients, with the server not doing much other than storing them.
Calculating Flags
Flags are only applicable to read states of type CHANNEL. Clients must calculate the flags based on the channel's properties before acknowledging a message.
If the calculated flags differ from the stored flags, clients must specify the flags field in the request.
Flags must be calculated as follows:
- If the channel has a
guild_id, set theIS_GUILD_CHANNELflag. - If the channel's
typeis one of the thread types (NEWS_THREAD,PUBLIC_THREAD,PRIVATE_THREAD), set theIS_THREADflag. - If the read state's
mention_countis comprised entirely of non-ping mentions, set theIS_MENTION_LOW_IMPORTANCEflag. 1
1 When the user has the MENTION_ON_ALL_MESSAGES setting enabled and the channel's notification level is set to ALL_MESSAGES, all messages in the channel increase the mention_count. However, these mentions are considered low importance as they do not ping the user. However, if there is at least one ping mention in the channel, the read state is not considered low importance.
Calculating Last Viewed
For read states of type CHANNEL, clients must calculate the last_viewed value before acknowledging a message.
The value is the number of days since the Discord epoch (January 1, 2015 00:00:00 UTC) at the time the channel was viewed.
If the calculated value differs from the stored value, clients must specify the last_viewed field in the request.
Deleting Read States
Clients should delete read states that are no longer relevant to the user. Official clients do this at application start, deleting read states that meet the following criteria:
- Read states of type
CHANNELfor channels that no longer exist (with a 30 day grace period to account for archived threads). - Read states of type
CHANNELfor channels the user no longer hasVIEW_CHANNELandREAD_MESSAGE_HISTORYpermissions in. - Read states of type
GUILD_EVENT,GUILD_HOME, orGUILD_ONBOARDING_QUESTIONfor guilds that the user is no longer a member of.
Make sure to ignore unrecognized read state and channel types to allow for forward compatibility.
Ack Tokens
Ack tokens are used to identify a specific client when interacting with read states.
The client should initially set its ack token to null when making the first ack request. The server will respond with a new token in the token field of the response, which the client should store locally.
When acknowledging a read state, the client should then send this token back to the server in the token field. The server will respond with an updated token, which the client should again store locally for future requests.
The client should reset the ack token to null whenever it switches accounts or a User Update Gateway event is received.
Read State Object
Read State Structure
| Field | Type | Description |
|---|---|---|
| id 1 | snowflake | The ID of the resource the read state is for |
| read_state_type? | integer | The type of read state (default CHANNEL) |
| last_message_id? 2 | snowflake | The ID of the last acknowledged message |
| last_acked_id? 2 | snowflake | The ID of the last acknowledged entity |
| mention_count? 3 | integer | The number of unread mentions |
| badge_count? 3 | integer | The number of unread badges |
| last_pin_timestamp? 4 | ISO8601 timestamp | When the last acknowledged pinned message was pinned |
| flags? 4 | integer | The read state flags |
| last_viewed? 4 | ?integer | When the resource was last viewed (in days since the Discord epoch) |
1 For user features, the resource ID is the current user's ID.
2 last_message_id and last_acked_id are mutually exclusive; only one will be present depending on the read_state_type, for backwards compatibility reasons.
3 mention_count and badge_count are mutually exclusive; only one will be present depending on the read_state_type, for backwards compatibility reasons.
4 Only applicable for read states of type CHANNEL.
Read State Type
| Value | Name | Description |
|---|---|---|
| 0 | CHANNEL | Channel message unreads |
| 1 | GUILD_EVENT | Guild scheduled event feature |
| 2 | NOTIFICATION_CENTER | Notification center feature |
| 3 | GUILD_HOME | Guild home feature |
| 4 | GUILD_ONBOARDING_QUESTION | Guild onboarding feature |
| 5 | MESSAGE_REQUESTS | Message requests feature |
Read State Flags
| Value | Name | Description |
|---|---|---|
| 1 << 0 | IS_GUILD_CHANNEL | Whether the channel is part of a guild |
| 1 << 1 | IS_THREAD | Whether the channel is a thread |
| 1 << 2 | IS_MENTION_LOW_IMPORTANCE | Whether channel mentions are of low importance |
Endpoints
Acknowledge Message
POST/channels/{channel.id}/messages/{message.id}/ackUpdates the channel's read state for the current user. Fires a Message Ack Gateway event.
The message ID parameter does not need to be a valid message ID, but it must be a valid snowflake. If the message ID is being set to a message sent prior to the latest acknowledged one, manual should be true or the resulting read state update might be ignored by clients, resulting in undefined behavior.
In this case, mention_count should also be updated to the amount of mentions unacknowledged as it is not automatically calculated by Discord.
JSON Params
| Field | Type | Description |
|---|---|---|
| token? | ?string | The last received ack token, or null |
| manual? | boolean | Whether the acknowleged message ID is manually set |
| mention_count? 1 | integer | The new unread indicator for the channel |
| flags? 2 | integer | The read state flags for the channel |
| last_viewed? 2 | integer | When the channel was last viewed (in days since the Discord epoch) |
1 Requires manual to be true.
2 If omitted, the current value is retained.
Response Body
| Field | Type | Description |
|---|---|---|
| token | ?string | The new ack token |
Acknowledge Pinned Messages
POST/channels/{channel.id}/pins/ackAcknowledges the currently pinned messages in a channel. Returns a 204 empty response on success. Fires a Channel Pins Ack Gateway event.
Acknowledge Guild
POST/guilds/{guild.id}/ackUpdates all read states in a guild to acknowledge all features and messages. Fires multiple Message Ack and Guild Feature Ack Gateway events.
Acknowledge Guild Feature
POST/guilds/{guild.id}/ack/{read_state.type}/{entity.id}Updates a guild feature's read state for the current user. Fires a Guild Feature Ack Gateway event.
JSON Params
| Field | Type | Description |
|---|---|---|
| token? | ?string | The last received ack token, or null |
Response Body
| Field | Type | Description |
|---|---|---|
| token | ?string | The new ack token |
Acknowledge User Feature
POST/users/@me/{read_state.type}/{entity.id}/ackUpdates a non-channel feature's read state for the current user. Fires a User Non Channel Ack Gateway event.
JSON Params
| Field | Type | Description |
|---|---|---|
| token? | ?string | The last received ack token, or null |
Response Body
| Field | Type | Description |
|---|---|---|
| token | ?string | The new ack token |
Bulk Update Read States
POST/read-states/ack-bulkUpdates multiple read states for the current user. Returns a 204 empty response on success. Fires multiple Message Ack, Guild Feature Ack, and User Non Channel Ack Gateway events.
JSON Params
| Field | Type | Description |
|---|---|---|
| read_states | array[read state update object] | The read state updates to update |
Read State Update Structure
| Field | Type | Description |
|---|---|---|
| read_state_type? | integer | The type of read state (default CHANNEL) |
| channel_id | snowflake | The ID of the resource the read state is for |
| message_id 1 2 | snowflake | The ID of the entity to set the read state to (message, guild scheduled event, etc.) |
1 Unlike standalone ack endpoints, the message ID must be greater than 0 or the read state update will be ignored.
2 As bulk updates do not accept a manual field, it is not recommended to set channel read states to a message ID lower than the current acknowledged one using this endpoint, as it will lead to undefined behavior.
Delete Read State
DELETE/channels/{read_state.id}/messages/ackDeletes a read state for the current user. Returns a 204 empty response on success.
JSON Params
| Field | Type | Description |
|---|---|---|
| read_state_type? | integer | The read state type to delete (default CHANNEL) |
| version? | integer | The version of the read state protocol the client has implemented, used to prevent accidental deletions from outdated clients (currently 2) |