Media Messages

Sending and receiving images, video, audio, documents, and stickers via Cloud API.

Overview

Media messages let you send non-text content through the same Cloud API message endpoint:

POST https://graph.facebook.com/<GRAPH_VERSION>/<PHONE_NUMBER_ID>/messages

Supported outbound types: image, video, audio, document, sticker.

In Dualhook architecture, sending is performed by your backend directly to Meta Graph API. Inbound media webhooks are delivered directly to your endpoint via Webhook Override. Dualhook does not proxy or store media binaries.

Supported Formats and Size Limits

TypeTypical supported formatsMax size
Imagejpeg, png5 MB
Audioaac, amr, mp3, m4a, ogg (Opus)16 MB
Videomp4, 3gp16 MB
Documentpdf, doc, docx, ppt, pptx, xls, xlsx, txt100 MB
StickerwebpStatic 100 KB, animated 500 KB
  • document, image, and video can include captions.
  • audio and sticker do not use captions.
  • Validate MIME type and file extension consistency before upload/send.

Send Media by Media ID

Recommended flow: upload media first to get a media_id, then send using id.

{
  "messaging_product": "whatsapp",
  "to": "<RECIPIENT_PHONE_NUMBER>",
  "type": "image",
  "image": {
    "id": "<MEDIA_ID>",
    "caption": "<OPTIONAL_CAPTION>"
  }
}

For document sends, include filename:

{
  "messaging_product": "whatsapp",
  "to": "<RECIPIENT_PHONE_NUMBER>",
  "type": "document",
  "document": {
    "id": "<MEDIA_ID>",
    "filename": "<DOCUMENT_FILENAME>",
    "caption": "<OPTIONAL_CAPTION>"
  }
}

For upload instructions, see Upload, Retrieve & Delete Media.

Send Media by Public URL

{
  "messaging_product": "whatsapp",
  "to": "<RECIPIENT_PHONE_NUMBER>",
  "type": "image",
  "image": {
    "link": "https://<PUBLIC_MEDIA_HOST>/<MEDIA_PATH>",
    "caption": "<OPTIONAL_CAPTION>"
  }
}

URL requirements:

  • URL must be publicly reachable over HTTPS.
  • The endpoint must return the binary file directly.
  • Content-Type should match the real file type.

Voice Messages vs Basic Audio

Audio messages have two presentations:

Voice messageBasic audio
Codec / format.ogg encoded with OPUS codec, monoAny supported audio format (mp3, aac, m4a, amr, etc.)
Auto-downloadYes (≤ 512 KB)No (user taps to download)
Profile pictureShows business profile imageGeneric music icon
IconMicrophoneMusic note (or microphone if file is OPUS-encoded .ogg)
Auto-transcriptionAvailable if user enabled "Automatic" voice transcriptsAvailable if file is OPUS-encoded .ogg and user enabled transcripts
Recipient sees a play iconYes (≤ 512 KB)Only if recipient has audio auto-download enabled and conditions are met (e.g. on Wi-Fi)

To send a voice message, set voice: true and provide an OPUS-encoded .ogg file:

{
  "messaging_product": "whatsapp",
  "to": "12015550123",
  "type": "audio",
  "audio": {
    "id": "<MEDIA_ID>",
    "voice": true
  }
}

If you set voice: true with a non-OPUS file, voice-message transcription will silently fail. To send a regular audio file, omit voice or set it to false.

Voice Played Status Webhook

Starting March 17, 2026, voice messages emit a new status webhook the first time a recipient plays a voice message you sent. The status is played and arrives via the standard messaging-status webhook envelope.

{
  "statuses": [
    {
      "id": "wamid.HBgL...",
      "status": "played",
      "timestamp": "1742227200",
      "recipient_id": "12015550123",
      "conversation": { "id": "<CONVERSATION_ID>" },
      "pricing": { /* same shape as other status webhooks */ }
    }
  ]
}

Only the first play triggers the webhook — subsequent replays do not. The webhook only fires for voice messages (audio with voice: true); basic audio messages do not get a played status.

In Dualhook setups, status webhooks are delivered directly to your endpoint via Webhook Override — Dualhook does not ingest them. Add played to the set of status values your handler recognizes if you want to track voice-message engagement.

Inbound Media Webhooks

Inbound media from users includes a media object with an ID. Your backend should use that media ID to retrieve the binary from Meta.

{
  "messages": [
    {
      "from": "<WHATSAPP_USER_PHONE_NUMBER>",
      "id": "<WHATSAPP_MESSAGE_ID>",
      "timestamp": "<TIMESTAMP>",
      "type": "image",
      "image": {
        "id": "<MEDIA_ID>",
        "mime_type": "image/jpeg",
        "sha256": "<MEDIA_SHA256>",
        "caption": "<OPTIONAL_CAPTION>"
      }
    }
  ]
}

Error Handling

  • Message API acceptance does not guarantee recipient delivery.
  • Final outcomes arrive via status webhooks.
  • Prefer media_id sends for higher reliability.
  • Keep retry logic with exponential backoff for 429 and 5xx.
  • Avoid tight retry loops for repeated 4xx validation failures.

Related

Browse more docsStart Free Trial