Mark conversation read
Chat
Mark conversation read
Adds a readBy receipt to every message in this conversation up to a cursor message id, and zeroes the caller’s unread counter.
POST
Mark conversation read
Overview
Records that the caller has read all messages in this conversation up to and includingupToMessageId. Two things happen server-side:
- Every message in this conversation created at or before
upToMessageId.createdAt, not sent by the caller, and without an existingreadByentry for the caller, gets areadByentry appended. The append is atomic and bulk: a singleupdateMany, not one update per message. - The caller’s
participantState[*].unreadCountis reset to0andlastReadMessageIdis set toupToMessageId.
newlyReceiptedCount in the response is the count of messages whose readBy was newly added in step 1 — useful for the client to decide whether to broadcast a chat:message:read socket event (a 0 count is a no-op and can be suppressed).
Mark-read is also available as a socket event (chat:read) — same service method, same dedup. See Socket events.
Authentication
Bearer <accessToken> required. requireOnboarded middleware applied.
Path parameters
| Name | Type | Required | Notes | Example |
|---|---|---|---|---|
id | string | yes | 24-char hex ObjectId of the conversation. | 66400a8f1c2b4d5e6f7a9000 |
Query parameters
None.Request body
| Field | Type | Required | Allowed values | Example |
|---|---|---|---|---|
upToMessageId | string | yes | 24-char hex ObjectId of a message in this conversation. Inclusive cursor: all messages with createdAt <= upToMessageId.createdAt get a receipt. | 66400a8f1c2b4d5e6f7aa001 |
Example payload
Response — 200 OK
| Field | Type | Notes | Example |
|---|---|---|---|
newlyReceiptedCount | integer | Number of messages whose readBy was newly added. Zero on a no-op (e.g. the caller had already marked everything read). | 1 |
Example response
Error responses
| Status | Code | Meaning |
|---|---|---|
| 400 | VALIDATION_FAILED | upToMessageId missing or not a 24-char hex ObjectId. |
| 401 | UNAUTHENTICATED | Missing, malformed, or expired access token. |
| 403 | ONBOARDING_INCOMPLETE | Caller has not finished onboarding. |
| 403 | FORBIDDEN | Caller is not a participant of this conversation. |
| 404 | NOT_FOUND | No conversation with that id. |
If
upToMessageId is a valid ObjectId but doesn’t reference a message in this conversation, the call returns 200 { "newlyReceiptedCount": 0 } (no rows match the bulk update). It does not 404 — the message id is a cursor hint, not a strict reference.Side effects
- Bulk
readByappend on the matchingmessagesrows (one DB round trip). participantState[caller].unreadCount = 0.participantState[caller].lastReadMessageId = upToMessageId.- (When called over the socket transport) a
chat:message:readbroadcast is emitted to theconversation:<id>room so the sender’s UI can update its delivery indicators in real time. The REST endpoint does not emit the broadcast — callers that want live read-receipts should use the socket event.
See also
- Socket events —
chat:read— the WebSocket variant; emitschat:message:readto the conversation room. - List messages — observe the
readByfield on each message. - Send message.