Skip to content

Media

Media is a third kind of send, alongside text and template, and follows the same window rule as text — there is no media template to fall back on.

Terminal window
curl -X POST "https://vaarto.com/api/v1/messages/whatsapp" \
-H "Authorization: Bearer $VAARTO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+919876543210",
"media": {
"type": "image",
"link": "https://yourcdn.com/invoice-1029.png",
"caption": "Your invoice"
}
}'
Field Notes
type image, video, audio, document or sticker
link a public HTTPS URL. Meta fetches it — the bytes never pass through this API
id media already uploaded to Meta. Give link or id, never both
caption shown under the attachment. Not accepted on audio or sticker
filename what the recipient’s phone saves it as. Documents only

An inbound attachment arrives as a media id, not a URL, and the URL Meta exchanges it for expires in minutes and needs the account’s token. So the bytes are streamed through this API instead:

Terminal window
curl -L "https://vaarto.com/api/v1/messages/whatsapp/MESSAGE_ID/media" \
-H "Authorization: Bearer $VAARTO_API_KEY" -o attachment.bin

Scope MEDIA_READ, not MESSAGES_READ — the log is text and status, this is the file itself.

It is also the one endpoint that does not answer in the { data } envelope: the response is the file, with content-type from Meta and a content-disposition filename for documents. GET /messages/whatsapp/:id tells you in advance whether there is anything to fetch, as hasMedia.

Attachments do not expire here. Meta keeps media for about 30 days and then stops serving it, so every attachment is copied into our own storage shortly after it arrives and this endpoint reads it back from there. A thread from last year opens its files exactly like a thread from this morning.

A 410 is therefore now rare, and means the attachment predates archiving or was already gone at Meta before it could be copied.

This covers attachments you sent as a link too. Meta fetches that URL itself, so nothing passed through this API at the time — the link is archived separately, and once it is, the attachment reads back like any other. Until then hasMedia is false and this endpoint answers 409 MEDIA_NOT_ARCHIVED. The URL must still be reachable when the copy is taken, so do not take it down the moment Meta has fetched it.

Failure HTTP Code
Never archived, and Meta’s 30 days have run out 410 MEDIA_EXPIRED
Not yet archived and Meta would not serve it 410 MEDIA_UNAVAILABLE
Sent as a link and not archived yet 409 MEDIA_NOT_ARCHIVED
Meta could not be reached, or refused 502 MEDIA_UNAVAILABLE