download
Download and decrypt media from a message.&dyn Downloadable
required
Any message type that implements the
Downloadable trait. Includes:ImageMessageVideoMessageAudioMessageDocumentMessageStickerMessageExternalBlobReference(app state)HistorySyncNotification
Vec<u8>
Decrypted media bytes. For encrypted media (E2EE), automatically decrypts using AES-256-CBC and verifies HMAC-SHA256. For plaintext media (newsletters/channels), validates SHA-256 hash.
Example: download image
Example: download with error handling
Automatic retry and URL re-derivation
All download methods handle three categories of CDN errors automatically:- Auth errors (401/403): The client invalidates the cached media connection, fetches fresh credentials, and retries the download once.
- Media not found (404/410): When a media URL has expired or the file has been relocated, the CDN returns 404 or 410. The client treats this the same as an auth error — it invalidates the cached connection, re-derives download URLs with fresh credentials and hosts, and retries once. This matches WhatsApp Web’s
MediaNotFoundErrorhandling. - Other errors (e.g., 500): The client tries the next available CDN host without refreshing credentials. Hosts are tried in priority order (primary first, then fallback).
download_to_writer), the writer is seeked back to position 0 before retrying so partial writes are overwritten. For in-memory downloads (download()), each retry gets a fresh buffer rather than reusing the previous one, so a failed host that wrote a longer body can’t leave a stale tail behind a shorter successful retry.
download_to_writer
Download media to a writer using streaming when available, with automatic buffered fallback. This is the method to use for downloading straight to a file — pass aFile or BufWriter<File>.
When the HTTP client supports streaming (supports_streaming() returns true), the entire HTTP download, decryption, and file write happen in a single blocking thread with ~40KB memory usage regardless of file size. When streaming is not available, the method automatically falls back to a buffered download — fetching the full response into memory, then decrypting and writing to the writer. This ensures download_to_writer works with any HttpClient implementation.
&dyn Downloadable
required
Message containing downloadable media
W: Write + Seek + Send + 'static
required
Writer for streaming output. Must be Send + ‘static for use in blocking task.
W
Returns the writer after successful download, seeked back to position 0.
Example: streaming download
When using an HTTP client that supports streaming (like the default
UreqHttpClient), memory usage is constant ~40KB (8KB read buffer + decryption state). HTTP clients that don’t support streaming fall back to buffered downloads, which load the full file into memory before writing.download_from_params
Download and decrypt media from raw CDN parameters without the original message. The parameters are bundled into aDownloadParams struct.
&DownloadParams
required
The CDN/crypto fields needed to fetch and decrypt the media. Build one with
DownloadParams::encrypted.Vec<u8>
Decrypted media bytes
Example: download from stored metadata
download_from_params_to_writer
Streaming variant ofdownload_from_params that writes to a writer.
&DownloadParams
required
The CDN/crypto fields needed to fetch and decrypt the media. See
DownloadParams.W
required
Writer for streaming output
W
Returns the writer after successful download
DownloadParams
ADownloadable built from raw CDN fields, for re-downloading media without the original message.
DownloadParams::encrypted
Convenience constructor for encrypted (E2EE) media — fillsmedia_key and file_enc_sha256 as Some(...).
DownloadParams implements Downloadable, so it works with download, download_to_writer, download_from_params, and download_from_params_to_writer.
fetch_sticker_pack
Fetch first-party sticker pack metadata (and the per-sticker download handles) from the WhatsApp CDN.&str
required
The first-party sticker pack ID (typically extracted from a received
sticker_pack_message).&str
required
BCP-47 locale tag for localized name / publisher strings. Pass
"en" to match whatsmeow’s default.StickerPack
Pack metadata plus a
Vec<StickerPackItem> of individual stickers. Each StickerPackItem implements Downloadable, so you can pass it straight to client.download(...).https://static.whatsapp.net/sticker?lottie=1&cat=sticker_pack_data&id={pack_id}&lg={locale}, parses the JSON envelope, and constructs the StickerPack. The endpoint is unauthenticated — the call works whether or not you are paired.
StickerPack
StickerPackItem
The CDN response is a JSON envelope, not a protobuf message —
StickerPack / StickerPackItem live in wacore::sticker_pack and are independent of waproto::whatsapp::StickerPackMessage (which represents the inline pack-bubble in a chat). The struct mirrors whatsmeow’s FirstPartyStickerPack.Downloadable Trait
TheDownloadable trait provides a generic interface for downloading media from any message type.
fn() -> Option<&str>
WhatsApp CDN path for the media file
fn() -> Option<&[u8]>
32-byte encryption key. Present for E2EE media,
None for plaintext (newsletter/channel) media.fn() -> Option<&[u8]>
SHA-256 hash of the encrypted file. Used for encrypted media validation.
fn() -> Option<&[u8]>
SHA-256 hash of the decrypted file. Used for plaintext media validation.
fn() -> Option<u64>
Original file size in bytes
fn() -> MediaType
Media type for HKDF key derivation (
Image, Video, Audio, Document, etc.)fn() -> Option<&str>
default:"None"
Static CDN URL for direct download. Present on newsletter/channel media, bypasses host construction.
fn() -> bool
default:"media_key().is_some()"
Returns
true if media is encrypted (has media_key), false for plaintext mediaBuilt-in Implementations
TheDownloadable trait is automatically implemented for:
wa::message::ImageMessagewa::message::VideoMessagewa::message::AudioMessagewa::message::DocumentMessagewa::message::StickerMessagewa::ExternalBlobReference(app state)wa::message::HistorySyncNotification
MediaType
Media type enum for encryption/decryption.Image/Sticker→"WhatsApp Image Keys"Video→"WhatsApp Video Keys"Audio→"WhatsApp Audio Keys"Document→"WhatsApp Document Keys"History→"WhatsApp History Keys"AppState→"WhatsApp App State Keys"StickerPack→"WhatsApp Sticker Pack Keys"StickerPackThumbnail→"WhatsApp Sticker Pack Thumbnail Keys"LinkThumbnail→"WhatsApp Link Thumbnail Keys"
MediaType methods
Upload paths
ProductCatalogImage is unencrypted — is_encrypted() returns false. This matches WhatsApp Web’s behavior where CreateMediaKeys.js skips encryption for product catalog images. Its upload path is /product/image (not under the /mms/ prefix like other media types).Media Decryption
WhatsApp uses different handling for encrypted (E2EE) and plaintext media:Encrypted Media (E2EE)
- Download encrypted bytes from CDN
- Verify HMAC-SHA256 (last 10 bytes)
- Decrypt using AES-256-CBC with keys derived from
media_keyvia HKDF - Return decrypted plaintext
media_key is expanded using HKDF-SHA256 to derive:
- 16-byte IV
- 32-byte cipher key
- 32-byte MAC key
Plaintext media (newsletter/channel)
- Download plaintext bytes from CDN (often via
static_url) - Verify SHA-256 hash matches
file_sha256 - Return plaintext (no decryption needed)
Newsletter and channel media is not encrypted. The library automatically detects this when
media_key is absent and switches to plaintext validation.Example: detect media type
DownloadUtils
Low-level static methods for media decryption and validation. These are re-exported fromwacore::download and useful when you need fine-grained control over the download pipeline.