Skip to main content

download

Download and decrypt media from a message.
Only use download when you need the plaintext bytes (processing, transcoding, re-upload). To forward existing media unchanged, reuse the original message’s CDN fields directly — no download required. See media forwarding via CDN reuse.
&dyn Downloadable
required
Any message type that implements the Downloadable trait. Includes:
  • ImageMessage
  • VideoMessage
  • AudioMessage
  • DocumentMessage
  • StickerMessage
  • ExternalBlobReference (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 MediaNotFoundError handling.
  • 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).
For streaming downloads (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 a File 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 a DownloadParams 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

DownloadParams implements Downloadable, so you can also pass it straight to download: client.download(&params).await?.

download_from_params_to_writer

Streaming variant of download_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

A Downloadable built from raw CDN fields, for re-downloading media without the original message.

DownloadParams::encrypted

Convenience constructor for encrypted (E2EE) media — fills media_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(...).
Under the hood the client GETs 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

The Downloadable 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 media

Built-in Implementations

The Downloadable trait is automatically implemented for:
  • wa::message::ImageMessage
  • wa::message::VideoMessage
  • wa::message::AudioMessage
  • wa::message::DocumentMessage
  • wa::message::StickerMessage
  • wa::ExternalBlobReference (app state)
  • wa::message::HistorySyncNotification

MediaType

Media type enum for encryption/decryption.
Each media type has specific HKDF info strings used for key derivation:
  • 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 unencryptedis_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)

  1. Download encrypted bytes from CDN
  2. Verify HMAC-SHA256 (last 10 bytes)
  3. Decrypt using AES-256-CBC with keys derived from media_key via HKDF
  4. Return decrypted plaintext
The media_key is expanded using HKDF-SHA256 to derive:
  • 16-byte IV
  • 32-byte cipher key
  • 32-byte MAC key

Plaintext media (newsletter/channel)

  1. Download plaintext bytes from CDN (often via static_url)
  2. Verify SHA-256 hash matches file_sha256
  3. 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 from wacore::download and useful when you need fine-grained control over the download pipeline.

Key methods