Skip to main content
Incoming messages arrive on the message event as a WaIncomingMessageEvent.

The event payload

WaIncomingMessageEvent carries a rich key (a superset of Proto.IMessageKey) plus a few top-level fields. Pass the event (or just its key) verbatim to reply / edit / react / revoke / pin / keep.
You also receive your own outgoing messages here (multi-device sync), flagged with key.fromMe === true. Filter them out if you only want inbound traffic.
The whole event (or event.key) is accepted as the target for replies/reactions/revokes/pins/keeps and as editKey for edits — no need to reshape it.

Extracting text

A message’s text lives in different fields depending on its type. A small helper covers the common cases:

Identifying the message type

message is a protobuf union — inspect which field is set:
To download media from an image/video/audio/document message, see Media → downloading.

Sending receipts

client.message.sendReceipt marks messages as received/read/played. The easiest form takes the event(s) directly:
You can also pass an array of events, or address it manually by and ids:

Calls

Read-only Incoming call signaling surfaces as the read-only call event (WaIncomingCallEvent). zapo reports calls — it does not place, accept, or reject them.
Useful fields: type (the signaling stage), callId, callCreatorJid / callerPnJid (who’s calling), isVideo, groupJid (group calls), and callerPushName. There is no API to answer a call.

Addons

Addons are encrypted follow-ups attached to a message: reactions, poll votes, and comments. They surface as the message_addon event.

Automatic decryption

Addons are decrypted and emitted for you by default — just subscribe to message_addon:

Manual decryption

Pass addons: { autoDecrypt: false } to receive the encrypted payload and decrypt on demand from the originating message event:

Protocol messages

Edits, revokes, and other protocol-level updates arrive on message_protocol as WaIncomingProtocolMessageEvent (it extends the message event with a protocolMessage field):

Requesting older history

The initial pairing flow streams a bounded window of message history. To pull older messages for a specific chat on demand, call client.message.requestHistorySync:
The method returns once the request is dispatched — not when the chunk arrives. The backfill is delivered later as a history_sync_chunk event, same as the bootstrap history. Subscribe before calling if you need to react to it:
Pair oldestMsgId, oldestMsgFromMe, and oldestMsgTimestampMs from the topmost message currently visible to page backwards correctly. Omit count to let the server apply its own default (~50).

Receipts (inbound)

When others read or play your messages, you receive receipt events:
event.messageIds is the full set of stanza ids this receipt acknowledges. WhatsApp batches read/delivery receipts into a single <receipt> carrying a <list><item id=…/> block — messageIds mirrors wa-web’s externalIds: list items first, then event.stanzaId appended last. Single-message receipts contain just [event.stanzaId].
receipt events still expose stanzaId / chatJid directly (they extend WaIncomingBaseEvent); the rename only applies to message, message_addon, and message_bot_chunk payloads, which now use event.key. event.stanzaId is still the last id in the batch — iterate messageIds to cover the rest.
Last modified on June 8, 2026