WABridges

Send and receive media

Images, video, audio, documents and stickers - sent by URL, received as a message id you download.

Sending is a URL, not an upload

There is no multipart upload. You give the bridge a public HTTP(S) URL and it fetches the file itself, which means the file has to be reachable from the internet - a signed S3 link, a public CDN path, an endpoint of your own.

The kind of message is decided by the Content-Type the URL responds with, not by the file extension or by a field you set. A URL that serves image/jpeg becomes an image; one that serves application/pdf becomes a document. A server that answers application/octet-stream for everything is the usual reason media arrives as a nondescript file.

curl
curl -X POST https://wabridges.com/api/instances/user-123/proxy/send/media \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "chat":    "15550001234",
    "url":     "https://example.com/photo.jpg",
    "caption": "check this out"
  }'
← 200
{
  "message_id": "ACE41E988162AB19A4A7BB7A9E663693",
  "timestamp": 1777180605
}

Every kind, same call

Only chat and url are required. caption is optional and ignored for kinds that cannot carry one.

Stickers are the one special case: set as_sticker and serve the file as image/webp. Anything else is rejected, and a caption on a sticker is dropped.

image
{"chat": "15550001234", "url": "https://example.com/photo.jpg", "caption": "check this out"}
document
{"chat": "15550001234", "url": "https://example.com/report.pdf", "caption": "Q1 report"}
voice note
{"chat": "15550001234", "url": "https://example.com/voice.ogg"}
sticker
{"chat": "15550001234", "url": "https://example.com/sticker.webp", "as_sticker": true}

Receiving: the event names the file, it does not carry it

An inbound photo arrives on your webhook as an ordinary message event with type: "media" and media_type saying which kind. The bytes are not in the payload - webhooks stay small and fast. What you get is the message_id, and that is the handle you download with.

A caption, when there is one, arrives in caption rather than body. An image sent with no caption has both empty, which is worth handling before you index on message text.

POST https://yourbackend.com/hook
{
  "event": "message",
  "type": "media",
  "media_type": "image",
  "body": "",
  "caption": "look at this",
  "chat_id": "15550001234@s.whatsapp.net",
  "contact_id": "15550001234@s.whatsapp.net",
  "from_me": false,
  "is_group": false,
  "message_id": "ACE7253B69494A9CB9D671C943A7F4AB",
  "name": "John Doe",
  "phone": "15550001234",
  "timestamp": 1777180605
}

Downloading what arrived

Fetch the bytes with the message_id from the event. The response is the raw file with its real content type - image/jpeg, audio/ogg, video/mp4, or application/octet-stream when it is anything else.

The bridge caches media for 3 hours. Inside that window this is cheap and repeatable; after it, the fetch can return 404. If you need to keep a file, copy it to your own storage when the webhook arrives rather than downloading it on demand later.

curl
curl https://wabridges.com/api/instances/user-123/proxy/messages/ACE7253B69494A9CB9D671C943A7F4AB/media \
  -H "Authorization: Bearer $WA_API_KEY" \
  --output inbound.jpg

Download in the background, not inside your webhook handler - the handler still has to answer 2xx quickly or deliveries queue up behind it.

Something here not matching what you see? Write to us - a person answers.