Media messages
The four media types
| type | Caption | Filename | Typical use |
|---|---|---|---|
image | Yes | No | A product shot, a receipt, a poster |
video | Yes | No | A short demo or announcement |
document | Yes | Yes — shown to the recipient | An invoice, a menu, a ticket |
audio | No | No | A 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 -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.
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
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 fordocument. It is what the recipient sees and what their phone saves.- On WhatsApp Web,
captionis the message text. A media send with no caption arrives with no words attached.
Size limits
| Channel | Maximum media size |
|---|---|
| Cloud API | 16 MB |
| WhatsApp Web | 64 MB |
Template header media is a different thing with different, tighter limits — see /docs/templates/media.
What can go wrong
Neither source given
{ "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
{ "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.