download
Download and decrypt media from a message.Any message type that implements the
Downloadable trait. Includes:ImageMessageVideoMessageAudioMessageDocumentMessageStickerMessageExternalBlobReference(app state)HistorySyncNotification
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_file
Download media and write directly to a file.Message containing downloadable media
Writer to output the decrypted data. Typically a
File or BufWriter<File>.Example: Download to File
download_to_writer
Download media using streaming when available, with automatic buffered fallback. 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.
Message containing downloadable media
Writer for streaming output. Must be Send + ‘static for use in blocking task.
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 parameters without the original message.WhatsApp CDN path (e.g.,
/v/t62.7118-24/12345_67890)32-byte media encryption key from message
SHA-256 hash of decrypted file
SHA-256 hash of encrypted file
Original file size in bytes
Type of media:
Image, Video, Audio, Document, Sticker, etc.Decrypted media bytes
Example: Download from Stored Metadata
download_from_params_to_writer
Streaming variant ofdownload_from_params that writes to a writer.
WhatsApp CDN path
32-byte media encryption key
SHA-256 hash of decrypted file
SHA-256 hash of encrypted file
Original file size in bytes
Type of media
Writer for streaming output
Returns the writer after successful download
Downloadable Trait
TheDownloadable trait provides a generic interface for downloading media from any message type.
WhatsApp CDN path for the media file
32-byte encryption key. Present for E2EE media,
None for plaintext (newsletter/channel) media.SHA-256 hash of the encrypted file. Used for encrypted media validation.
SHA-256 hash of the decrypted file. Used for plaintext media validation.
Original file size in bytes
Media type for HKDF key derivation (
Image, Video, Audio, Document, etc.)Static CDN URL for direct download. Present on newsletter/channel media, bypasses host construction.
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
| Method | Return type | Description |
|---|---|---|
app_info() | &'static str | HKDF info string for key derivation |
mms_type() | &'static str | Media type string for MMS path construction |
upload_path() | &'static str | URL path prefix for upload/download |
is_encrypted() | bool | Whether this media type uses E2E encryption |
Upload paths
| Media type | Upload path |
|---|---|
Image / Sticker | /mms/image |
Video | /mms/video |
Audio | /mms/audio |
Document | /mms/document |
History | /mms/md-msg-hist |
AppState | /mms/md-app-state |
StickerPack | /mms/sticker-pack |
StickerPackThumbnail | /mms/thumbnail-sticker-pack |
LinkThumbnail | /mms/thumbnail-link |
ProductCatalogImage | /product/image |
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.
Key methods
| Method | Description |
|---|---|
verify_and_decrypt(encrypted_payload, media_key, media_type) | Verifies HMAC and decrypts AES-256-CBC in one call |
decrypt_stream(reader, media_key, media_type) | Streaming decryption from a reader (convenience wrapper around decrypt_stream_to_writer) |
decrypt_stream_to_writer(reader, media_key, media_type, writer) | Streaming decryption directly into a writer with constant memory usage |
validate_plaintext_sha256(data, expected_sha256) | Validates SHA-256 hash of plaintext media (in-memory) |
copy_and_validate_plaintext_to_writer(reader, expected_sha256, writer) | Streams plaintext media to a writer while validating SHA-256 hash |
prepare_download_requests(downloadable, media_conn) | Builds CDN request URLs with host failover |
get_media_keys(media_key, app_info) | Derives IV, cipher key, and MAC key via HKDF |