Media messages
Images, audio, video, documents and stickers all send the same way: upload the file once to get a mediaId, then send that mediaId. Splitting the upload from the send lets you reuse one uploaded file across many recipients and keeps the send body tiny.
Step 1 — upload the file
Post the raw file as multipart form-data (field name file) to the channel's media route. Telenow uploads it to WhatsApp and hands back a mediaId — a Telenow id (UUID), not a Meta media id — plus the file's details.
curl -X POST https://api.telenow.ai/api/app-whatsapp/channels/b1e2c3d4-…/media \
-H "Authorization: Bearer vai_app_…" \
-F "[email protected]"
{
"success": true,
"data": {
"mediaId": "5f0c2b1e-8d4a-4b6e-9c3f-2a7d1e0b9c84",
"mime": "application/pdf",
"filename": "invoice-1234.pdf",
"bytes": 84213
}
}
The message type is inferred from the MIME type you upload with — the Content-Type of the multipart part (curl sets it from the file name; set it yourself in code). You never pass a type: application/pdf becomes a document, image/jpeg an image, image/webp always a sticker. A part sent without a type is treated as application/octet-stream and goes as a document. If the file exceeds its cap you get a 413 (WhatsApp caps image/jpeg files at 5 MB) before anything is sent (see caps).
- A
mediaIdbelongs to the channel it was uploaded on — sending it on another channel fails (upload not found for this channel). - WhatsApp keeps the uploaded file for about 30 days; upload again after that.
- Uploads work on Meta Cloud numbers (native or your own Meta app). Other numbers return
400. - Don't send an
Idempotency-Keyheader on uploads larger than 5 MB — with that header the request body is capped at 5 MB (413 body too large).
Step 2 — send the mediaId
Post to /send with the mediaId from step 1. Add a caption for image, video or document; omit it for audio and stickers.
curl -X POST https://api.telenow.ai/api/app-whatsapp/send \
-H "Authorization: Bearer vai_app_…" \
-H "Content-Type: application/json" \
-d '{
"channelId": "b1e2c3d4-…",
"to": "+919876543210",
"mediaId": "5f0c2b1e-8d4a-4b6e-9c3f-2a7d1e0b9c84",
"caption": "Your invoice for order #1234"
}'
{ "success": true, "data": { "wamid": "wamid.HBgLMTQxNTU1MDEwMBUCABIYFj…" } }
Media is free-form content, so it only sends inside the 24-hour customer service window; outside it, start with a template. As with every send, track delivery via whatsapp.message.status, and use an Idempotency-Key header (or dedupe on the wamid) to make retries safe — see Text messages. A mediaId that isn't a UUID is refused with 422 before anything is sent.
Media types
Each type has its own size cap and its own rule about captions. A caption is a text string that rides along with the file; WhatsApp only supports it on visual/attachment types.
Image
JPEG or PNG, up to 5 MB. Caption allowed. The caption renders under the image and is the natural place for a short line about what the customer is looking at.
Video
MP4, up to 16 MB. Caption allowed. Same placement as an image caption.
Document
Any file (PDF, spreadsheet, etc.), up to 100 MB. Caption allowed — it shows as the message text above the file card. The original filename from your upload is what the customer sees and downloads.
Audio
Up to 16 MB. No caption — WhatsApp renders audio as a standalone playable clip with no accompanying text, so a send that includes a caption is refused; send the text as a separate message.
Sticker
WebP, up to 500 KB. No caption. Stickers are square, transparent-background images and, like audio, carry no text. Any image/webp upload is sent as a sticker.
Size caps & captions
| Type | Max size | Caption? |
|---|---|---|
| Image | 5 MB | Yes |
| Video | 16 MB | Yes |
| Document | 100 MB | Yes |
| Audio | 16 MB | No |
| Sticker | 500 KB | No |
Exceeding a cap fails the upload with
413— the file never reaches the send step. Acaptionon audio or a sticker is rejected (502,WhatsApp doesn't allow a caption on audio messages — send the text separately), not dropped.
Quote a message
Add reply_to (the wamid of the message you're answering) to the send body to thread your media under it — the same field documented in Reply to a specific message. It works alongside mediaId, location, interactive, and every other content type:
{ "channelId": "b1e2c3d4-…", "to": "+919876543210",
"mediaId": "5f0c2b1e-8d4a-4b6e-9c3f-2a7d1e0b9c84",
"caption": "Here's your receipt.",
"reply_to": "wamid.HBgLMTQx…" }
Receiving media
When a customer sends you a photo, voice note, or file, it arrives on the whatsapp.message.received webhook. Telenow fetches the bytes first and then sends the event once: content.media describes the attachment (id, mime, filename), and the top-level media object carries a signedUrl you can download from for about 1 hour (it can be null if the link couldn't be made). If the fetch failed, media holds a fetchError instead of the link.
{
"event": "whatsapp.message.received",
"channelId": "b1e2c3d4-…",
"messageId": "e5b2c9a4-1f7d-4c8a-9b2e-0a1b2c3d4e5f",
"from": "+919876543210",
"wamid": "wamid.HBgLM…",
"type": "image",
"content": {
"media": { "id": "1122334455", "mime": "image/jpeg", "filename": "photo.jpg" }
},
"media": {
"mime": "image/jpeg",
"filename": "photo.jpg",
"signedUrl": "https://…"
},
"timestamp": "2026-07-18T09:31:00+00:00"
}
To download later (after the signed link expires), use GET /media/{id}. The {id} is the Telenow message id (a UUID) — the messageId in the webhook above, or the id from GET /api/app-whatsapp/threads/{id}/messages (ready-made in media.url). It is not the wamid, and not content.media.id.
curl -L https://api.telenow.ai/api/app-whatsapp/media/e5b2c9a4-1f7d-4c8a-9b2e-0a1b2c3d4e5f \
-H "Authorization: Bearer vai_app_…" \
-o inbound-photo.jpg
The response streams the raw bytes as an attachment (Content-Disposition: attachment with the file name, X-Content-Type-Options: nosniff, Cache-Control: private, no-store); SVG, XML and HTML files are always served as application/octet-stream. See Webhooks & events for the full received payload and signature verification.
With your org API key
With your org API key (X-API-Key: vai_live_…) the same three steps use the org routes; the request and response bodies are the same, except that the channel goes in the path:
| Step | Org-key route |
|---|---|
| Upload | POST /api/orgs/{orgId}/whatsapp-hub/channels/{channelId}/media — multipart field file, same { mediaId, mime, filename, bytes } back |
| Send | POST /api/orgs/{orgId}/whatsapp-hub/channels/{channelId}/send — { "to", "mediaId", "caption"?, "reply_to"? } (or …/threads/{threadId}/send without to) |
| Download | GET /api/orgs/{orgId}/whatsapp-hub/media/{messageId} — images, audio and video are served inline, other files as attachments |
curl -X POST https://api.telenow.ai/api/orgs/{orgId}/whatsapp-hub/channels/{channelId}/media \
-H "X-API-Key: vai_live_…" \
-F "[email protected]"
Media needs a Meta Cloud number (native or your own Meta app) on both surfaces; on other numbers the upload returns 400 and a media send returns 502. Uploads and sends need an owner, admin or developer key.
Related
- Text messages — plain
messagetext and the 24-hour window. - Sending template messages — media headers and sends outside the 24-hour window.
- Interactive messages — reply buttons, lists, and CTA URLs.
- Webhooks & events — receive inbound media and track delivery.
- WhatsApp overview — connect a number and find your
channelId.