mark_as_read
Send read receipts for one or more messages. Read receipts inform the sender that you’ve read their message(s). For group messages, you must pass the original sender’s JID as thesender parameter.
message_ids is a borrowed slice &[&str] to avoid per-call allocations — pass &["ID"] or &["ID_1", "ID_2"].&Jid
required
Chat JID where the messages were received. Can be:
- Direct message:
15551234567@s.whatsapp.net - Group:
120363040237990503@g.us
Option<&Jid>
Message sender JID. Required for group messages,
None for direct messages.- For DMs: Pass
None - For groups: Pass the JID of the user who sent the message(s)
&[&str]
required
List of message IDs to mark as read. Can be a single ID or multiple IDs.If empty, this function returns immediately without sending anything.
Example: mark DM as read
Example: mark group message as read
Example: mark multiple messages as read
Read receipts are not sent automatically by the library. You must explicitly call
mark_as_read() when you want to notify the sender that messages have been read.mark_as_read adapts the wire shape to the chat, matching WhatsApp Web. A newsletter read is sent as read-self. A status@broadcast read carries context="status". When the status author is a LID, it also adds peer_participant_pn (the resolved LID→PN). For a direct message, the receipt also downgrades to read-self when the account’s readreceipts privacy setting is none — the sender is not notified, matching WhatsApp Web. Groups and broadcast lists always send the notifying read type regardless of that setting. The same call handles all these cases — you don’t pass anything extra. See Read receipt privacy gating below.mark_as_played
Send played receipts for one or more voice notes or video notes. Played receipts tell the sender that you’ve listened to their voice note or watched their video note — they’re the equivalent of “read” for playable media. Call this only after the user has actually played the media; if you just want to acknowledge that the message was opened, usemark_as_read instead.
message_ids is a borrowed slice &[&str], matching mark_as_read.&Jid
required
Chat JID where the media message was received. Can be a direct message, group, broadcast list, or newsletter JID.
Option<&Jid>
Original sender JID.
- For DMs: pass
None(theparticipantattribute is dropped on the wire, matching WhatsApp Web). - For groups, broadcast lists, and status broadcasts: pass the JID of the user who sent the media.
- For newsletters: the receipt is sent as
played-self;senderis ignored.
&[&str]
required
Message IDs of the voice or video notes to mark as played. The first ID becomes the receipt’s
id attribute; any additional IDs are batched into a <list><item/></list> child, the same shape as mark_as_read.If empty, this function returns immediately without sending anything.Example: mark a DM voice note as played
Example: mark a group voice note as played
Example: mark multiple voice notes as played
Played receipts are not sent automatically — call
mark_as_played() from your media player when the user finishes (or starts) playing the audio or video note. Like mark_as_read, a DM played receipt downgrades to played-self when the account’s readreceipts privacy setting is none (the sender is not notified); groups, broadcast lists, and status broadcasts are unaffected by that setting. See Read receipt privacy gating below.Read receipt privacy gating
Thereadreceipts privacy category (see PrivacyCategory::ReadReceipts) controls whether mark_as_read and mark_as_played notify the other party for direct messages:
This gate only applies to one-on-one direct messages. Group messages, broadcast lists, and status broadcasts always send the notifying
read/played type — WhatsApp Web does not apply this privacy setting to them. Newsletters are unaffected too; they always use read-self/played-self regardless of this setting.
The value is populated from a background call to fetch_privacy_settings that the client runs automatically after connecting, and is persisted on the device. On a reconnect where the setting hasn’t changed since the last fetch, the persisted value already reflects it, so the correct receipt type is available before that connect’s background fetch completes. If the setting did change — from another linked device, or from this client’s own set_privacy_setting call — the persisted value is stale until that reconnect’s background fetch runs and updates it.
Toggle the setting with
client.set_privacy_setting(PrivacyCategory::ReadReceipts, PrivacyValue::None).await? (or PrivacyValue::All to re-enable). See the privacy API for details. This call only updates the setting on the server — the local cache mark_as_read/mark_as_played read from is refreshed only by the background fetch that runs on connect, so a receipt sent later in the same session, before the next connect, can still go out as the old type. Calling fetch_privacy_settings() yourself does not refresh that cache either — like set_privacy_setting, it’s a bare passthrough that returns the current server values without touching device state. Reconnecting (or restarting) is currently the only way to guarantee the new gating applies within the running process.send_delivery_receipt (Internal)
Sends a delivery receipt to the sender of a message. This is an internal method called automatically by the library when messages are received. You typically don’t need to call this directly.&Arc<MessageInfo>
required
Message metadata containing:
id- Message IDsource.chat- Chat JIDsource.sender- Sender JIDsource.is_from_me- Whether this is your own messagesource.is_group- Whether this is a group message
Behavior
Delivery receipts are automatically sent for all incoming messages except:- Your own messages (
is_from_me = true) - Messages without an ID
- Status broadcast messages (
status@broadcast) - Newsletter messages
participant attribute identifying the sender.
Delivery receipts are sent automatically. Unlike other receipt types (e.g.,
type="read", type="played"), delivery receipts have no type attribute on the wire — delivery is the implicit default. The library omits the type attribute from ack responses to delivery receipts accordingly, since including an explicit type="delivery" would cause <stream:error> disconnections from the server. This is different from read receipts (type="read"), which you send manually with mark_as_read().Wire format
Internally, delivery receipts pass JID references directly to the.attr() method, avoiding allocations on the hot path:
v0.6 split the
to attribute by addressing case. Earlier versions always used info.source.chat, which strips the device byte (to_non_ad). For multi-device LID senders that arrived with from="USER:DEV@lid", the device-less receipt was rejected by the LID server: it replayed the stanza from the offline queue and eventually closed the stream with <stream:error><ack class="message"/></stream:error>. Matching whatsmeow’s buildBaseReceipt (which echoes node.Attrs["from"] verbatim) and WA Web’s sendDeliveryReceiptsAfterDecryption resolves the issue.mark_as_read) batch multiple message IDs using a <list> child node with <item> elements:
Receipt Types
WhatsApp supports multiple receipt types:variant
Delivery receipt (type=
""). Confirms message was delivered to the recipient’s device. Sent automatically by the library.variant
Read receipt (type=
"read"). Confirms message was read by the recipient. Sent manually via mark_as_read().variant
Read receipt from your own device (type=
"read-self"). Received when you read a message on another device, or sent for a DM when your own readreceipts privacy setting is none (see Read receipt privacy gating).variant
Played receipt (type=
"played"). Confirms media (audio/video) was played by the recipient.variant
Played receipt from your own device (type=
"played-self"). Received when you play media on another device, or sent for a DM when your own readreceipts privacy setting is none (see Read receipt privacy gating).variant
Retry receipt (type=
"retry"). Recipient failed to decrypt the message and is requesting a retry. Automatically handled by the library.variant
VoIP call encryption re-keying retry receipt (type=
"enc_rekey_retry"). Sent when a peer fails to decrypt VoIP call encryption data and needs the sender to re-key. Uses an <enc_rekey> child element (with call-creator, call-id, count attributes) instead of the standard <retry> child. Automatically handled by the library.variant
Sender receipt (type=
"sender"). Acknowledges message was sent.variant
Server error receipt (type=
"server-error"). Message delivery failed on server.Receipt Events
You can listen for receipt events to track message delivery and read status using the Bot event handler:Receipt event structure
Receipt is #[non_exhaustive] and constructed internally via a bon builder (Receipt::builder()…build()), so a struct-pattern destructure needs a .. rest — e.g. Receipt { source, r#type, .. }. See Payload stability for the full policy.MessageSource
Source information:
chat- Chat JID where the receipt originatedsender- JID of the user who sent the receiptis_group- Whether this is from a group
bool
true when the receipt carried the offline attribute. That means it was drained from the server’s offline queue on reconnect rather than delivered live. Use it to tell a backlog of receipts received at login apart from real-time ones.Vec<String>
List of message IDs this receipt applies to. Usually contains a single ID, but can have multiple.
DateTime<Utc>
When the receipt was received (local time)
ReceiptType
Type of receipt (Delivered, Read, Played, etc.)
Message tracking example
Track message delivery and read status:Played receipts (media)
For voice notes and video notes, send played receipts withmark_as_played once the user has played the media. Newsletters send played-self; everything else sends played, unless the DM privacy gate (see above) downgrades it to played-self. The wire shape mirrors read receipts:
Event::Receipt event with ReceiptType::Played or ReceiptType::PlayedSelf.