Send by URL
The simplest path: give us a public URL and we fetch, type and send it. The media kind is taken from mimeType, the response's Content-Type, or the file name, in that order.
POST https://api.meowsapp.com/send-media-message
{
"clientId": "acmemain",
"to": "123456789012345",
"downloadUrl": "https://files.example.com/brochure.pdf",
"fileName": "Brochure.pdf",
"caption": "Here is the brochure",
"quotedMessageId": "3EB0…"
}
// → { "status": "sent", "messageId": "3EB0…", "mediaType": "document", "bytes": 482113 }Send by upload
For files you hold in memory, upload them as multipart/form-data. Set the part's Content-Type correctly — it decides image/video/audio/document.
curl -X POST "https://api.meowsapp.com/send-media-message?clientId=acmemain" \
-H "Authorization: Bearer $SN_TOKEN" \
-F "[email protected];type=image/jpeg" \
-F clientId=acmemain -F to=123456789012345 -F caption="Here you go"Multipart needs ?clientId= in the URL
Requests are routed to the machine holding the session by reading clientId from the query string or JSON body. Form bodies cannot be read by the router, so multipart calls must repeat clientId in the URL.
Voice notes & stickers
// voice note (push-to-talk bubble) — supply OGG/Opus
{ "clientId": "acmemain", "to": "123456789012345", "downloadUrl": "https://files.example.com/reply.ogg", "mimeType": "audio/ogg; codecs=opus", "sendAudioAsVoice": true }
// sticker — supply WebP (512×512 recommended)
{ "clientId": "acmemain", "to": "123456789012345", "downloadUrl": "https://files.example.com/thumbs-up.webp", "asSticker": true }Audio without sendAudioAsVoice is delivered as a regular audio file. There is no transcoding: convert to OGG/Opus (e.g. ffmpeg -i in.mp3 -c:a libopus -b:a 32k out.ogg) before sending a voice note.
Receiving media
Webhook media events carry descriptors, not bytes:
{
"type": "image",
"messageId": "3EB0…",
"body": "Here you go",
"media": {
"mimeType": "image/jpeg", "mediaType": "image", "fileName": "photo.jpg", "fileSize": 102400, "caption": "Here you go",
"download": { "directPath": "/v/t62.7118-24/…", "mediaKey": "base64…", "fileSha256": "base64…", "fileEncSha256": "base64…", "fileLength": 102400 }
}
}You have two options:
- Download and decrypt yourself with any WhatsApp Web client library — they all take exactly these fields. Treat
mediaKeyas a secret; the CDN path expires after some days, so download promptly. - Use the hosted inbox. Point the session's webhook at the hosted receiver and every file is downloaded, decrypted and stored under a permanent public link within seconds;
downloadUrlappears on the message inGET /chatrooms/{id}/messages. Voice notes keep theiraudio/ogg; codecs=opustype and play in any modern browser.
Limits
| Limit | Value |
|---|---|
| Send by URL — fetch cap | 100 MB, 60 s timeout |
| Send by URL — over 60 MB | Delivered as a text message containing the link ("fallback": "text") instead of failing |
| Upload — ingress cap | 128 MB request; keep files ≤ 64 MB for WhatsApp's own limits |
| Images | WhatsApp re-encodes to JPEG; send PNG/JPEG for photos, WebP with asSticker for stickers |
| Documents | Any type; set fileName so the recipient sees a sensible name |