Media
Upload a file to send in a message, and get short-lived links to files.
Files never pass through your API request body. You ask Gauge for an upload URL, upload the file straight to storage, then send a message that refers to it.
Ask for an upload URL
POST /v2/media · scope media:write · requires Idempotency-Key · live, unpinned key
{ "contentType": "application/pdf", "sizeBytes": 183402, "filename": "invoice-1042.pdf", "connectionId": "9b2f6c1e-3d4a-4e5b-8f70-1a2b3c4d5e6f" }| Field | |
|---|---|
contentType | One of image/jpeg, image/png, image/webp, image/gif, video/mp4, video/3gpp, audio/ogg, audio/opus, audio/webm, audio/mpeg, audio/mp4, audio/aac, audio/amr, application/pdf. Anything else answers 400. |
sizeBytes | The exact size. It is signed into the upload URL: storage refuses a body of any other length. |
filename | Optional, up to 200 characters. |
connectionId | Optional but recommended: the file is checked against that connection's limits now rather than at send time. |
201 answers the media with a presigned upload:
{
"id": "5d1c7e2a-9b3f-4a6d-8e0c-2f4a6b8d0e1f",
"contentType": "application/pdf",
"sizeBytes": 183402,
"filename": "invoice-1042.pdf",
"status": "pending",
"createdAt": "2026-10-21T09:30:00.000Z",
"expiresAt": "2026-10-21T09:45:00.000Z",
"upload": {
"url": "https://…",
"method": "PUT",
"headers": { "Content-Type": "application/pdf", "Content-Length": "183402" },
"expiresAt": "2026-10-21T09:45:00.000Z"
},
"download": null
}Upload the file
PUT the bytes to upload.url with exactly upload.headers, within 15 minutes. The URL is
valid for that one file only.
curl -X PUT "$UPLOAD_URL" -H "Content-Type: application/pdf" -H "Content-Length: 183402" --data-binary @invoice-1042.pdfSend it
Use the media id as mediaId in POST /v2/messages, on a connection whose
capabilities.sendMedia is true. text becomes the caption. A send before the upload landed
answers 400 with reason media_not_uploaded.
Limits
| Largest file | |
|---|---|
Any upload (no connectionId) | 16 MB |
| WhatsApp Cloud and WhatsApp Web | 16 MB; photos on WhatsApp Cloud 5 MB |
| Every other channel that sends files | 7 MB |
Over the limit answers 413 payload_too_large with reason file_too_large and details.maxBytes.
A connection that cannot send files answers 422 unsupported_channel_action with reason
media_not_supported. Each connection states its own limit as limits.maxMediaBytes on
GET /v2/connections/{id}.
Download a file you uploaded
GET /v2/media/{id} · scope media:read
Once the file has landed, status is uploaded and download is a signed link valid for
5 minutes. Fetch the file when you need it rather than storing the link. Until then download is
null. A key sees the files its member uploaded, or every file when its member sees every
conversation.
Files on messages (what a customer sent you, or any file on a message) are downloaded through the
message: GET /v2/messages/{id}/attachments/{index}.
That is the link a webhook's media[].url carries.
When file storage is not available
File uploads and downloads need storage the internet can reach. Where a deployment has none
configured yet, POST /v2/media, GET /v2/media/{id} and
GET /v2/messages/{id}/attachments/{index} answer 503 with code media_unavailable. It is a
state of the deployment, not of your request: text, template and interactive sends are unaffected.
Retry later.
Which channels carry files today: Channels.