未读数与已读
服务端为每个用户在每个会话中记录一个已读位置(用户会话对象中的 read_seq),并据此计算未读数。客户端没有拉取过的消息也计入未读,所以服务端给出的未读数总是准确的。
你的业务服务端可以:
- 查询用户的未读总数,例如在用户长时间没有登录、仍有未读消息时,通过短信或邮件提醒他;
- 代用户标记已读,例如用户已经在你的网页、邮件等其他渠道看过了这条通知。
代用户标记已读就是真正的已读,效果与用户在客户端标记相同:他的所有设备随之更新,单聊的对方会看到新的已读位置,群里要求已读回执的消息计入已读。
未读数的计算
- 未读数是已读位置之后、用户能看到的、计入未读的消息条数。已读位置只增不减,读到后面的消息就表示读过了前面的。
- 以下消息不计入未读:群提示;发送时
exclude_from_unread为true的消息;用户自己在客户端发送的消息;已超过保留期被删除的消息。 - 从服务端以某个用户的身份发送消息时(见发送消息):他原本没有未读消息的,已读位置随之前进到这条消息;原本有未读的,已读位置不变,这条消息也计入他自己的未读数。
- 用户删除的消息仍计入未读,直到已读位置越过它。代用户删除还没读的消息时,请同时代他标记已读,见删除会话与消息。
- 删除会话、清空聊天记录时,已读位置前进到当时的最后一条,未读数清零。
- 用户在客户端把会话“标记为未读”时,已读位置和未读数都不变,只是
marked_unread变为true;再次标记已读时自动取消。 - 群里 @ 了用户或 @ 全体成员的消息,在用户读到它之前,
mention_seq为这条消息的序号,读到之后变为null。
已读的同步效果
标记已读,以及同样会推进已读位置的删除会话、清空聊天记录,会产生以下效果:
| 对象 | 标记已读、删除会话、清空聊天记录 | 全部标记已读 |
|---|---|---|
| 用户自己的在线设备 | 实时收到这个会话的新状态,未读数、@ 提醒随之更新 | 收到需要重新同步的通知,随后同步会话列表 |
| 用户的离线设备 | 下次登录或同步时更新 | 同左 |
| 单聊的对方 | 对方会话中的 peer_read_seq 前进到新的已读位置,在线设备实时收到已读通知 | peer_read_seq 同样前进,但不实时通知,对方在下次同步时看到 |
| 群里要求已读回执的消息 | 计入已读人数(已离开群的用户不计入);群不超过 500 人时,消息的发送者实时收到已读通知 | 同左 |
- 单聊的已读位置由应用的运行策略
single_read_ack_enabled控制(默认开启),关闭后不更新、不返回peer_read_seq,也不通知对方。群已读回执由group_read_ack_enabled控制(默认关闭),只对发送时要求了回执的消息生效。 - 重复标记(已读位置没有前进,对方看到的已读位置也已是最新)时,不通知对方,也不影响已读回执。
- 从服务端以用户的身份发送消息,不会推进单聊对方看到的已读位置,也不计入群已读回执。需要表示他已经读过时,另外调用标记会话已读。
查询用户的未读总数
查询一个用户全部会话的未读数之和与有未读的会话数,可用于 App 图标上的角标或未读提醒。
/{org_name}/{app_name}/users/{username}/unread-count统计的范围:用户当前所在的全部群,以及按最后一条消息的时间排序、最近的 1000 个单聊和已离开的群。会话更多时只统计最近的 1000 个,truncated 为 true。处于删除状态(hidden 为 true)的会话不计入。
路径参数
org_name、app_name 见接入概述。
| 参数 | 类型 | 说明 |
|---|---|---|
username | String | 用户名,不区分大小写 |
请求示例
curl "$IM_API/$ORG/$APP/users/bob/unread-count" \
-H "Authorization: Bearer $APP_TOKEN"响应
成功返回 200 OK:
| 字段 | 类型 | 说明 |
|---|---|---|
total | Number | 全部会话的未读数之和 |
conversation_count | Number | 有未读消息的会话数 |
marked_unread_count | Number | 没有未读消息、但被用户标记为未读的会话数 |
truncated | Boolean | 单聊和已离开的群超过 1000 个,只统计了最近的 1000 个 |
{
"total": 3,
"conversation_count": 1,
"marked_unread_count": 1,
"truncated": false
}错误
| HTTP 状态码 | code | 说明 |
|---|---|---|
| 404 | not_found | 用户不存在或已删除 |
标记会话已读
代用户把一个会话标记为已读:把他的已读位置推进到指定的消息,同时取消“标记为未读”。效果见已读的同步效果。
/{org_name}/{app_name}/users/{username}/conversations/{conversation_id}/read已离开的群也可以标记已读,只改变这个用户自己的状态。
路径参数
org_name、app_name 见接入概述。
| 参数 | 类型 | 说明 |
|---|---|---|
username | String | 用户名,不区分大小写 |
conversation_id | String | 会话 ID;群会话的 ID 就是群 ID |
请求体
请求体可以省略,省略时标记到最新一条。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
seq | Number | 否 | 已读到的消息序号。不传或为 null 时为用户能看到的最新一条;大于最新一条时按最新一条处理;不大于当前已读位置时已读位置不变 |
请求示例
curl -X POST "$IM_API/$ORG/$APP/users/bob/conversations/99582660229201920/read" \
-H "Authorization: Bearer $APP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"seq": 5
}'响应
成功返回 200 OK:
| 字段 | 类型 | 说明 |
|---|---|---|
conversation | Object | 操作之后的用户会话对象 |
changed | Boolean | 是否有变化。已读位置没有前进、没有取消“标记为未读”、单聊对方看到的已读位置也不需要更新时为 false,可以放心重试 |
{
"conversation": {
"conversation_id": "99582660229201920",
"conversation_type": "single",
"peer": "alice",
"group_id": null,
"membership": null,
"start_seq": 1,
"max_seq": 10,
"read_seq": 5,
"unread_count": 5,
"marked_unread": false,
"peer_read_seq": 0,
"mention_seq": null,
"cleared_seq": 0,
"hidden_seq": 0,
"hidden": false,
"pinned_at": null,
"joined_at": null,
"left_at": null,
"message_version": 0,
"last_message_at": "2026-10-02T19:07:06.211Z",
"version": 12
},
"changed": true
}调用之后,对方 alice 的会话中 peer_read_seq 变为 5,她的在线设备实时收到 bob 已读到第 5 条的通知。
错误
| HTTP 状态码 | code | 说明 |
|---|---|---|
| 400 | invalid_argument | 请求体不是 JSON 对象,seq 不是数字,或包含其他字段 |
| 404 | not_found | 用户不存在或已删除;会话不存在;单聊中用户不是会话的一方;群不存在,或者是私有群而用户从未入群 |
| 403 | not_group_member | 公开群,用户不是成员,也没有离开之前的记录;details.reason 为 leave_pending 时是用户刚离开群、离开的处理还没完成,稍后重试 |
全部标记已读
代用户把全部有未读的会话标记为已读,范围与未读总数相同:当前所在的群,以及最近的 1000 个单聊和已离开的群中,有未读消息或被标记为未读的会话。
/{org_name}/{app_name}/users/{username}/conversations/read-all用户的在线设备收到需要重新同步的通知;单聊的对方不会逐个收到已读通知,在下次同步时看到新的已读位置,见已读的同步效果。
路径参数
org_name、app_name 见接入概述。
| 参数 | 类型 | 说明 |
|---|---|---|
username | String | 用户名,不区分大小写 |
请求示例
curl -X POST "$IM_API/$ORG/$APP/users/bob/conversations/read-all" \
-H "Authorization: Bearer $APP_TOKEN"响应
成功返回 200 OK:
| 字段 | 类型 | 说明 |
|---|---|---|
count | Number | 这次标记为已读的会话数。没有未读的会话时为 0 |
truncated | Boolean | 单聊和已离开的群超过 1000 个,更早的会话没有处理。这些会话可以用标记会话已读逐个处理 |
{
"count": 2,
"truncated": false
}错误
| HTTP 状态码 | code | 说明 |
|---|---|---|
| 404 | not_found | 用户不存在或已删除 |
