Media
Pre-upload-once media so you can fan a single image / audio / document to many recipients without re-uploading every time.
{ "source_url": "https://cdn.example.com/promo.jpg", "mime_type": "image/jpeg" }Returns { media_id, media_url, mime_type, size }. Plug media_url into any subsequent message-send body's media_url field. The 25 MB cap matches the per-message cap WhatsApp enforces for audio / document.
Inbound media
Inbound media flows through a signed proxy at GET /v1/media/:token — the token is the authorisation, so your front-end can hot-link to it without exposing API keys.
Retention
We keep inbound media for a limited window and then delete the bytes. The token's expiresAt is the same deadline as the retention of the file it points at, so a token that still looks valid always resolves to bytes we still hold.
Past the window the media is gone for good — plan on persisting the bytes on your side. You will see:
| Status | Code | Meaning |
|---|---|---|
| 401 | media_token_expired | The token is past expiresAt. |
| 410 | media_gone | We no longer store this media. |
Re-fetch by message id
If your own ingestion failed and the webhook's media_url has already expired, re-fetch the same bytes with your channel API key — no signed token involved. :messageId is the message_id from the webhook payload.
Responds with the raw bytes and the original Content-Type. Returns 404 message_not_found when that message isn't on the channel, and 410 media_gone once retention has dropped it.