client.message.send(to, content, options?). The content argument is a WaSendMessageContent:
type discriminator — the library validates and fills protocol fields for you) or a raw Proto.IMessage (you build the protobuf yourself). The same send() accepts both.
Shorthand
A plain string is sent as a text message:Typed builders
Each builder is discriminated by itstype field. Bold fields are required.
Text & media
Media builders also accept any non-managed field of the underlying protobuf message (e.g.
caption, gifPlayback, ptt, fileName) via the UserMediaFields mapping. Protocol-managed fields (url, mediaKey, fileSha256, directPath, …) are filled by the builder.
mimetype is optional. The resolution order is: an explicit mimetype you pass wins; otherwise the builder calls media.processor.detectMimetype (provided by @zapo-js/media-utils when file-type is installed); otherwise it throws for image/video/audio/document/ptv. Stickers default to image/webp.
Interactive
target is a WaMessageTargetInput — a WaMessageKey ({ remoteJid, id, fromMe, participant? }) or a received message event passed verbatim (its key is used). poll/event parents additionally require authorJid and the 32-byte messageSecret.
For revoke, sender-vs-admin is auto-detected from target.fromMe (false triggers an admin revoke). There is no subtype option.
Raw Proto.IMessage
For anything without a typed builder, pass a raw protobuf message. The library inspects the populated field and automatically resolves the stanza attributes — message type ([resolveMessageTypeAttr]), media type, polltype, event_type, view_once, and edit — so you only set the content field.
Text
Media
Raw media fields require pre-uploaded media (the encryption keys,
directPath, and digests must already be set). To upload from bytes/a file, use the typed media builders instead — they perform the upload for you.Location & contacts
Interactive & business
Polls & events (raw)
Poll creation and event messages auto-persist their
messageSecret so later votes/responses can be encrypted.
Protocol, edits & system
Wrappers
These wrap an inner message; the library unwraps them when resolving attributes:ephemeralMessage, viewOnceMessage, viewOnceMessageV2, deviceSentMessage, groupMentionedMessage, botInvokeMessage, documentWithCaptionMessage.
For view-once specifically, prefer the viewOnce send option over hand-wrapping.
Raw proto cookbook
Concreteclient.message.send(jid, …) payloads for the common raw kinds. Import proto for enum values:
Plain text
Text with mentions + reply
Text with a manual link preview
Static location
Live location
Contact card (vCard)
Multiple contacts
Group invite
Buttons (raw)
Up to three quick-reply buttons. The header is aoneof — pick text, image, video, location, or document (pre-uploaded for media):
List menu (raw)
A single-select list of rows grouped into sections:Interactive native flow (raw)
The modern interactive surface — buttons whose params are JSON-encoded ad-hoc payloads:Product (raw)
Send a catalog product. The innerproductImage must already be uploaded:
Order (raw)
Order confirmation / inquiry:Newsletter admin invite (raw)
Invite a contact to co-admin one of your newsletters:Reaction (raw)
Pin / unpin (raw)
Keep / un-keep in chat (raw)
Revoke / delete-for-everyone (protocolMessage)
Toggle disappearing messages (ephemeral setting)
Poll (raw)
Request a phone number
Disappearing wrapper (ephemeralMessage)
Wrap any message so it inherits the chat’s ephemeral timer:View-once wrapper
Snippets that quote a message use a
key of { remoteJid, fromMe, id, participant? } (a proto.IMessageKey). For the typed equivalents (reaction, revoke, pin, keep, poll) prefer the builders — they manage the message secret and key wiring for you.