Media messages

Images, video, documents and audio. Supply the file as a URL we fetch, or as bytes we store for you — the wire format is the same either way.

The four media types

typeCaptionFilenameTypical use
imageYesNoA product shot, a receipt, a poster
videoYesNoA short demo or announcement
documentYesYes — shown to the recipientAn invoice, a menu, a ticket
audioNoNoA voice note. Rendered as a player, not a file

Media is a free-form message, so on Cloud API the 24-hour window applies exactly as it does to text. There is no such thing as a media template send through this endpoint.

Sending by URL

The simplest path. The URL must be publicly reachable — Meta fetches it, and a URL behind your own authentication will fail there rather than here.

cURL
curl -X POST https://api.wpai.co.in/api/messages/send \
  -H "X-API-Key: $WPAI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "919876543210",
    "type": "document",
    "mediaUrl": "https://files.example.com/invoices/4471.pdf",
    "filename": "Invoice-4471.pdf",
    "caption": "Your invoice for order 4471."
  }'

Sending inline bytes

When you hold the file rather than a link, send it inline. We store it, give it a public URL, and send that. Both a bare base64 string and a full data URL work — the MIME type is read from the data URL when present.

Node
import { readFile } from 'node:fs/promises';

const bytes = await readFile('./invoice-4471.pdf');

await fetch('https://api.wpai.co.in/api/messages/send', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.WPAI_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    to: '919876543210',
    type: 'document',
    // Or just bytes.toString('base64') — the data URL prefix only sets the MIME type.
    base64: `data:application/pdf;base64,${bytes.toString('base64')}`,
    filename: 'Invoice-4471.pdf',
  }),
});

mediaUrl wins

Supply both and mediaUrl is used; the base64 payload is ignored, not stored. Without a data URL prefix the stored file gets application/octet-stream, which some clients will refuse to preview — pass the prefix, or set filename with a real extension.

The request body limit is 25 MB, and base64 inflates bytes by roughly a third — so a file much over 18 MB needs the URL route regardless of the channel's own ceiling.

Captions and filenames

  • caption (max 1024) is the text shown with the media, and it is what gets recorded as the message body in the conversation history.
  • filename (max 255) is only meaningful for document. It is what the recipient sees and what their phone saves.
  • On WhatsApp Web, caption is the message text. A media send with no caption arrives with no words attached.

Size limits

ChannelMaximum media size
Cloud API16 MB
WhatsApp Web64 MB
From each channel’s capability set. Meta additionally enforces per-format limits of its own.

Template header media is a different thing with different, tighter limits — see /docs/templates/media.

What can go wrong

Neither source given

400
{ "error": "Parameter \"mediaUrl\" or \"base64\" is required for a media message" }

The window is closed

Same 400 as a text message. Media is free-form, so it is governed by the same rule, and there is no template equivalent for arbitrary media.

Meta rejected the file

502
{ "error": "Failed to send media message: (#131053) Media upload error" }

Usually an unreachable URL, an unsupported format, or a file over Meta's own limit. The message carries Meta's own text — worth logging in full.