Skip to content

Media upload components

Updated: Apr 15, 2026

WhatsApp does not guarantee that data (such as images, videos, or documents) shared with you by your customers is non-malicious. Make sure to implement appropriate risk mitigations when processing such data (for example, using well-tested and up-to-date media and document processing libraries).

Flow JSON components

Two components can be used to ask users to upload media:

  • PhotoPicker: allows uploading media from camera or gallery
  • DocumentPicker: allows uploading media from files or gallery

PhotoPicker

Supported starting with Flow JSON version 4.0.

ParameterDescription
type (required) stringPhotoPicker
name (required) stringComponent's name. Must be distinct among all components on a screen.
label (required) stringHeader text for the component. Dynamic "${data.label}" OR "${screen.<screen_id>.data.label}" Max length: 80 characters
description stringBody text for the component. Dynamic "${data.description}" OR "${screen.<screen_id>.data.description}" Max length: 300 characters
photo-source enumSpecifies the source where the image can be selected from. Values: {'camera_gallery', 'camera', 'gallery'} Default: 'camera_gallery' * camera_gallery: user can select from gallery or take a photo * gallery: user can select only from gallery * camera: user can only take a photo
max-file-size-kb IntegerSpecifies the maximum file size (in kibibytes) that can be uploaded. Default value: 25600 (25 MiB) Allowed range: [1, 25600]
min-uploaded-photos IntegerSpecifies the minimum number of photos that are required. This property determines whether the component is optional (set to 0) or required (set above 0). Default value: 0 Allowed range: [0, 30] Note: Above limits apply if media files are sent to the endpoint via "data_exchange" action. For images or documents sent as part of the response message, no more than 10 files can be attached. Additionally, the aggregated size of them cannot exceed 100 MiB.
max-uploaded-photos IntegerSpecifies the maximum number of photos that can be uploaded. Default value: 30 Allowed range: [1, 30] Note: Above limits apply if media files are sent to the endpoint via "data_exchange" action. For images or documents sent as part of the response message, no more than 10 files can be attached. Additionally, the aggregated size of them cannot exceed 100 MiB.
enabled *BooleanString*
visible *BooleanString*
error-message *StringObject*

Example

Flow JSON

{

"version": "7.3",

"data_api_version": "3.0",

"routing_model": {

"FIRST": []

},

"screens": [

{

"id": "FIRST",

"title": "Photo Picker Example",

"terminal": true,

"data": {},

"layout": {

"type": "SingleColumnLayout",

"children": [

{

"type": "Form",

"name": "flow_path",

"children": [

{

"type": "PhotoPicker",

"name": "photo_picker",

"label": "Upload photos",

"description": "Please attach images about the received items",

"photo-source": "camera_gallery",

"min-uploaded-photos": 1,

"max-uploaded-photos": 10,

"max-file-size-kb": 10240

},

{

"type": "Footer",

"label": "Submit",

"on-click-action": {

"name": "data_exchange",

"payload": {

"images": "${form.photo_picker}"

}

}

}

Enter to Rename, ⇧Enter to Preview

Preview

Run

Settings


Preview Flow

Photo Picker Example

Photo Picker Example

Upload photos

Please attach images about the received items

Add 1 to 10 photos. Max file size 10 MB.

Take photo

Submit

Managed by the business. Learn more Learn more

Note that the image selection behavior is mocked in this preview. The actual behavior on device will be similar to the image selection in WhatsApp chats.

Limitations and restrictions

The table below outlines the constraints associated with the PhotoPicker component.

ConstraintValidation error
min-uploaded-photos should not exceed max-uploaded-photos"min-uploaded-photos" cannot be greater than "max-uploaded-photos" for PhotoPicker component ${component_name}.
PhotoPicker cannot be initialized using Form init-valuesInvalid value found for property at ${path}. "init-values" property should not contain a value for PhotoPicker component.
Only 1 PhotoPicker is allowed per screenYou can only have a maximum of 1 component of type PhotoPicker per screen.
Using both PhotoPicker and DocumentPicker components on a single screen is not allowed.You can only have a maximum of 1 component of type PhotoPicker or DocumentPicker per screen.
The PhotoPicker is not allowed in the navigate action payload. To access the component's value from a different screen, you can use Global Dynamic Referencing.The PhotoPicker component's value is not allowed in the payload of the navigate action.
The PhotoPicker component is restricted to top-level usage within the payloads of the data_exchange or complete action. Valid: "on-click-action": { "name": "data_exchange", "payload": { "media": "${form.photo_picker}" } } Invalid: "on-click-action": { "name": "data_exchange", "payload": { "media": {"photo": "${form.photo_picker}"} } }The PhotoPicker can only be used as the value of a top-level string property in the action payload.
No more than 10 images or documents can be sent as part of the response message.Additionally, the maximum aggregated size of attached images or documents cannot exceed 100 MiB.

DocumentPicker

Supported starting with Flow JSON version 4.0.

ParameterDescription
type (required) stringDocumentPicker
name (required) stringComponent's name. Must be distinct among all components on a screen.
label (required) stringHeader text for the component. Dynamic "${data.label}" OR "${screen.<screen_id>.data.label}" Max length: 80 characters
description stringBody text for the component. Dynamic "${data.description}" OR "${screen.<screen_id>.data.description}" Max length: 300 characters
max-file-size-kb IntegerSpecifies the maximum file size (in kibibytes) that can be uploaded. Default value: 25600 (25 MiB) Allowed range: [1, 25600]
min-uploaded-documents IntegerSpecifies the minimum number of documents that are required. This property determines whether the component is optional (set to 0) or required (set above 0). Default value: 0 Allowed range: [0, 30] Note: Above limits apply if media files are sent to the endpoint via "data_exchange" action. For images or documents sent as part of the response message, no more than 10 files can be attached. Additionally, the aggregated size of them cannot exceed 100 MiB.
max-uploaded-documents IntegerSpecifies the maximum number of documents that can be uploaded. Default value: 30 Allowed range: [1, 30] Note: Above limits apply if media files are sent to the endpoint via "data_exchange" action. For images or documents sent as part of the response message, no more than 10 files can be attached. Additionally, the aggregated size of them cannot exceed 100 MiB.
allowed-mime-types Array <string>Specifies which document mime types can be selected. If it contains "image/jpeg", picking photos from the gallery will be available as well. Default: Any document from the supported mime types can be selected Supported values: * application/gzip * application/msword * application/pdf * application/vnd.ms-excel * application/vnd.ms-powerpoint * application/vnd.oasis.opendocument.presentation * application/vnd.oasis.opendocument.spreadsheet * application/vnd.oasis.opendocument.text * application/vnd.openxmlformats-officedocument.presentationml.presentation * application/vnd.openxmlformats-officedocument.spreadsheetml.sheet * application/vnd.openxmlformats-officedocument.wordprocessingml.document * application/x-7z-compressed * application/zip * image/avif * image/gif * image/heic * image/heif * image/jpeg * image/png * image/tiff * image/webp * text/plain * video/mp4 * video/mpeg Note: some old Android and iOS OS versions don't understand all mime types above. As a result, a user might be able to select a file with a different mime type to the ones specified.
enabled *BooleanString*
visible *BooleanString*
error-message *StringObject*

Example

Flow JSON

{

"version": "7.3",

"data_api_version": "3.0",

"routing_model": {

"SECOND": []

},

"screens": [

{

"id": "SECOND",

"terminal": true,

"title": "Document Picker Example",

"data": {},

"layout": {

"type": "SingleColumnLayout",

"children": [

{

"type": "Form",

"name": "flow_path",

"children": [

{

"type": "DocumentPicker",

"name": "document_picker",

"label": "Contract",

"description": "Attach the signed copy of the contract",

"min-uploaded-documents": 1,

"max-uploaded-documents": 1,

"max-file-size-kb": 1024,

"allowed-mime-types": [

"image/jpeg",

"application/pdf"

]

},

{

"type": "Footer",

"label": "Submit",

"on-click-action": {

"name": "complete",

"payload": {

"documents": "${form.document_picker}"

Enter to Rename, ⇧Enter to Preview

Preview

Run

Settings


Preview Flow

Document Picker Example

Document Picker Example

Contract

Attach the signed copy of the contract

Max file size 1 MB.

Upload document

Submit

Managed by the business. Learn more Learn more

Note that the document selection behavior is mocked in this preview. The actual behavior on device will be similar to the document selection in WhatsApp chats.

Limitations and restrictions

The table below outlines the constraints associated with the DocumentPicker component.

ConstraintValidation error
min-uploaded-documents should not exceed max-uploaded-documents"min-uploaded-documents" cannot be greater than "max-uploaded-documents" for DocumentPicker component ${component_name}.
DocumentPicker cannot be initialized using Form init-valuesInvalid value found for property at ${path}. "init-values" property should not contain a value for DocumentPicker component.
Only 1 DocumentPicker is allowed per screenYou can only have a maximum of 1 component of type DocumentPicker per screen.
Using both PhotoPicker and DocumentPicker components on a single screen is not allowed.You can only have a maximum of 1 component of type PhotoPicker or DocumentPicker per screen.
The DocumentPicker is not allowed in the navigate action payload. To access the component's value from a different screen, you can use Global Dynamic Referencing.The DocumentPicker component's value is not allowed in the payload of the navigate action.
The DocumentPicker component is restricted to top-level usage within the payloads of the data_exchange or complete action. Valid: "on-click-action": { "name": "data_exchange", "payload": { "media": "${form.document_picker}" } } Invalid: "on-click-action": { "name": "data_exchange", "payload": { "media": {"document": "${form.document_picker}"} } }The DocumentPicker can only be used as the value of a top-level string property in the action payload.
No more than 10 images or documents can be sent as part of the response message.Additionally, the maximum aggregated size of attached images or documents cannot exceed 100 MiB.

Handling media

Endpoint

Media uploaded by the users is temporarily (up to 20 days) stored in WhatsApp CDN. Files are encrypted using AES256-CBC+HMAC-SHA256+pkcs7 cryptographic algorithms.

In your endpoint implementation, you must download, decrypt, and validate each media file.

Here's a payload example for a photo or document.

"photo_picker":[{  
     "media_id": "790aba14-5f4a-4dbd-aa9e-0d75401da14b",  
     "cdn_url": "https://mmg.whatsapp.net/v/redacted",  
     "file_name": "IMG_5237.jpg"  
     "encryption_metadata": {  
       "encrypted_hash": "/QvkBvpBED2q2AHPIFuhXfLpkn22zj2kO6ggzjvhHv0=",  
       "iv": "5SHjLrrsfPXTSJTcbrVSkg==",  
       "encryption_key": "lPa4SXcWbk3sy2so3OxjyXmpV4aE6CcIKd+4byr5hBw=",  
       "hmac_key": "15l+E9Z5gcL15WH9OQ8GgK7VVCKkfbVigoSiM9djvGU=",  
       "plaintext_hash": "AOF2dHXVEpm9efk9udNy3R1cUJWnpjFwQKGBEdALqXI="  
     }]

Decrypting and validating media

The files stored in WhatsApp CDN contain the encrypted media and the first 10 bytes of the HMAC-SHA256 (concatenated at the end). For reference, cdn_file = ciphertext & hmac10.

Perform the following steps to decrypt the media:

  • Download cdn_file file from cdn_url
  • Make sure SHA256(cdn_file) == enc_hash
  • Validate HMAC-SHA256
    • Calculate HMAC with hmac_key, initialization vector (encryption_metadata.iv) and ciphertext
    • Make sure first 10 bytes == hmac10
  • Decrypt media content
    • Run AES with CBC mode and initialization vector (encryption_metadata.iv) on ciphertext
    • Remove padding (AES256 uses blocks of 16 bytes, padding algorithm is pkcs7). Call this decrypted_media
  • Validate the decrypted media
    • Make sure SHA256(decrypted_media) = plaintext_hash

Response message (Cloud API)

Media can be received in the response message webhook.

Here's a truncated example using PhotoPicker (same structure for DocumentPicker)

{  
    "nfm_reply": {  
        // [... redacted ... ]  
        "response_json": {  
            "photo_picker": [  
                {  
                    "file_name": "IMG_5237.jpg",  
                    "mime_type": "image/jpeg",  
                    "sha256": "PqHgadp8cJ/N6mvAYGNMxhs9Ra5hbZFcctCtCClXsMU=",  
                    "id": "3631120727156756"  
                }  
            ],  
            "flow_token": "xyz",  
            "name": "John"  
        }  
    }  
}

The media can be downloaded following the same steps as for regular image and document messages.

Unofficial mirror for reference/search purposes. All content originates from developers.facebook.com — see the source link at the top of each page. Machine-readable indexes: llms.txt · llms-full.txt · About