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.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:
Sending receipts
client.message.sendReceipt marks messages as received/read/played. The easiest form takes the event(s) directly:
Calls
Read-only Incoming call signaling surfaces as the read-onlycall event (WaIncomingCallEvent). zapo reports calls — it does not place, accept, or reject them.
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 themessage_addon event.
Automatic decryption
Addons are decrypted and emitted for you by default — just subscribe tomessage_addon:
Manual decryption
Passaddons: { 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 onmessage_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, callclient.message.requestHistorySync:
history_sync_chunk event, same as the bootstrap history. Subscribe before calling if you need to react to it:
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 receivereceipt 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.