Skip to main content

Overview

waproto contains the Protocol Buffers definitions for all WhatsApp message types. It’s auto-generated from whatsapp.proto using buffa and provides strongly-typed Rust structs for working with WhatsApp’s binary protocol.

Structure

The build writes whatsapp.rs and tags.rs to OUT_DIR. Do not commit these files. Consumers of the crate never need protoc installed — only editors of whatsapp.proto do, to regenerate the committed descriptor.

Usage

All protobuf types are under the waproto::whatsapp module:
Sub-message fields (nested protobuf messages) are buffa::MessageField<T>, not Option<Box<T>> or Option<T>. Construct one with buffa::MessageField::some(..), and read it back with .as_option(), .is_set(), or .is_unset().
waproto re-exports buffa (waproto::buffa), and whatsapp-rust in turn re-exports it as whatsapp_rust::buffa (with MessageField also available directly from prelude). Naming buffa::MessageField no longer requires a direct buffa dependency of your own — see Installation. You can still add buffa directly if you want to pin your own version or use APIs beyond MessageField.
Plain scalar fields (String, Vec<u8>, bool, integers) are still Option<T>, same as before. Enum fields are a typed Option<EnumType> rather than a raw Option<i32>, and enum variants are SCREAMING_SNAKE_CASE (e.g. wa::message::protocol_message::Type::MESSAGE_EDIT).

Key message types

Core message types

Message

The main message container used for all WhatsApp messages.
Usage in main library:

MessageKey

Identifies a specific message in a conversation.

Media Messages

ImageMessage

VideoMessage

AudioMessage

DocumentMessage

StickerMessage

Rich content messages

ExtendedTextMessage

Text with formatting, links, and quoted messages.

InteractiveMessage

Buttons, lists, and other interactive elements.

ButtonsMessage

ListMessage

Encryption Messages

SenderKeyDistributionMessage

Used for group message encryption.

PreKeySignalMessage

Used for establishing 1:1 encryption.

System & protocol messages

ProtocolMessage

For protocol-level operations.
Common types:
  • REVOKE - Revoke sent message
  • MESSAGE_EDIT - Edit sent message
  • EPHEMERAL_SETTING - Ephemeral message setting

ReactionMessage

EditMessage

AlbumMessage

Parent message for grouped media albums. Declares expected image/video counts so WhatsApp clients know how many items to group together.
Each child media message is wrapped in associated_child_message (a FutureProofMessage) and linked to the parent via a MessageAssociation:
Use whatsapp_rust::proto_helpers::wrap_as_album_child to construct album children. See Sending Messages - Album messages for usage examples.

AI & bot messages

AIRichResponseMessage

BotFeedbackMessage

Metadata & Context

MessageContextInfo

ContextInfo

Quoted messages, mentions, and forwarding info.

Device & identity types

ADV Messages

Account Device Verification messages.
These ADV types are capitalized ADV... (not Adv...) to match buffa’s acronym-preserving type naming.

Signal protocol structures

These are all-scalar structs (no nested messages), so they’re unaffected by the MessageField change above.

Handshake & connection types

HandshakeMessage

Used during initial connection handshake.

ClientPayload

Device and client information during pairing.

History sync types

HistorySyncNotification

HistorySync

Media reference types

ExternalBlobReference

References to uploaded media files.

App state types

SyncActionValue

App state synchronization actions.

Enums

waproto generates real Rust enums (not raw i32 constants). Variant names are SCREAMING_SNAKE_CASE:
buffa generates a closed enum by default: an unrecognized wire value decodes to None rather than being preserved, unlike the old prost-generated Option<i32> fields which round-tripped any integer. In practice WhatsApp only ever sends in-schema values, so this only matters for forward compatibility with brand-new server-side enum variants.SyncdMutation.operation (SyncdOperation) is the one deliberate exception: build.rs opts it into buffa’s open enum mode, so the field type is Option<buffa::EnumValue<SyncdOperation>> instead of Option<SyncdOperation>. An unrecognized wire value decodes to EnumValue::Unknown(n) rather than None. wacore_appstate’s process_patch rejects an unknown operation with a typed AppStateError::UnsupportedSyncdOperation before mutating any state, instead of silently treating it as SET and corrupting the app-state LTHash — which is what the previous closed-enum decode did.

Feature flags

The generate feature was removed in #836. Code generation is now always-on — buffa-build, buffa-descriptor, heck, and sha2 are unconditional build dependencies. Remove --features generate from any build scripts.

Serde support

All generated types derive serde::Serialize by default. Deserialization and snake_case renaming are behind optional feature flags (serde-deserialize and serde-snake-case above). Enable them in your Cargo.toml:

Default behavior (no feature flags)

All types derive Serialize only:
This allows JSON serialization for debugging:

With serde-deserialize

All types also derive Deserialize with #[serde(default)], matching protobuf semantics where missing fields use default values:

With serde-snake-case

All types additionally accept snake_case during deserialization. This primarily affects enum and oneof variants (buffa generates SCREAMING_SNAKE_CASE names), while struct fields are already snake_case. Serialization output remains unchanged.
The serde-snake-case feature is primarily useful for WASM bridge scenarios where JavaScript sends snake_case JSON to the Rust backend. For most Rust-only use cases, you only need the default Serialize support.

waproto::tags

The build generates the waproto::tags module from the compiled protobuf descriptor. You get one pub mod per proto message with one pub const FIELD_NAME: u32 = N; per field. Nested messages produce nested modules.
The history-sync wire walkers use these constants internally, and compile-time assert! blocks pin them in the hand-written mirror structs. If whatsapp.proto renumbers a field the consts update automatically on next build; if a field referenced by the walkers is renamed or removed, compilation fails rather than the decoder silently reading the wrong wire data.

Code generation

The build generates protobuf code — build.rs always runs. It reads the committed binary descriptor (src/whatsapp.desc), verifies its SHA-256 against src/whatsapp.desc.sha256, and hands it to buffa-build, writing two files into OUT_DIR:
  • whatsapp.rs — full buffa-generated structs and enums, including zero-copy view types
  • tags.rswaproto::tags field-number constants (see waproto::tags above)
whatsapp.proto stays in its upstream camelCase form; buffa-build’s idiomatic_field_names option converts field and oneof identifiers to snake_case Rust idents at codegen time (word boundaries match heck/prost), so the generated API keeps the prost-style names build.rs used to produce by hand. buffa-descriptor and heck remain build dependencies — they back the waproto::tags generation described above, which reads the original (camelCase) descriptor directly. Neither generated file is committed. buffa-build, buffa-descriptor, heck, and sha2 are unconditional build dependencies. protoc is only needed to regenerate the descriptor after editing whatsapp.proto — normal builds of waproto (or anything depending on it) never invoke protoc. To update after modifying whatsapp.proto:
The build aborts with a clear message if the descriptor SHA-256 does not match.
The generate feature flag was removed. Code generation is now always-on and does not require any feature flags. Remove --features generate from any existing build scripts.

Usage Examples

Constructing Messages

Pattern Matching

MessageField doesn’t pattern-match like Option directly — match on .as_option() instead:

Media Downloads

See Media Handling for complete examples.

WhatsApp Version

The protobuf definitions are based on:
This version is automatically included in the generated file header.

Relationship with wacore

wacore provides utilities for working with waproto messages:
  • proto_helpers - Conversion between protobuf and internal types
  • download - Downloadable trait for media messages
  • upload - Media encryption for upload
  • messages - Message encryption/decryption
  • send - Message building and sending
Example:

Next Steps

wacore

Platform-agnostic protocol implementation

Sending Messages

Sending and receiving messages

Media Handling

Working with media uploads and downloads

Signal Protocol

End-to-end encryption details