Sign inGet started

Receiving WhatsApp images

A photo or graphic a contact sends arrives as an inbound message carrying image: a reference to the file Bird stored for you, plus whatever caption they typed under it.

What an inbound image carries

代码示例
{
  "id": "wam_01kya19eknftrs2s6p82asmvnh",
  "direction": "inbound",
  "from": { "phone_number": "+14155550100" },
  "to": { "phone_number": "+13124495569" },
  "status": "received",
  "image": {
    "id": "waf_01kyb2m4xq7whs0d8n3prv6tez",
    "url": "https://platform.bird.com/v1/whatsapp/messages/wam_01kya19eknftrs2s6p82asmvnh/media/waf_01kyb2m4xq7whs0d8n3prv6tez",
    "mime_type": "image/jpeg",
    "caption": "Is this the right part?"
  },
  "created_at": "2026-08-25T09:04:11Z"
}
FieldWhat it carries
idThe stored file, to pass as media_id when fetching the bytes
urlA Bird URL, fetched with your API key
mime_typeThe media type WhatsApp reported for the file, such as image/jpeg
captionThe text the contact typed under the image; absent when they sent none
id and mime_type appear only on an inbound image: Bird learns both by fetching the file, and never stored the one you sent. An outbound image reads back with the url you supplied and no id.
Treat mime_type as WhatsApp's report rather than a guarantee, and branch on it instead of on the file extension in url, which carries none.

Fetching the bytes

url and id both point at the same stored file: pass the message ID and the media id to the channel's media method, in any SDK, the CLI, or cURL. The hub's fetching inbound media carries that call in every language, along with the redirect and header rules it follows.
The message and its media expire together, 30 days after the message arrives; the hub's fetching inbound media owns that window and what the reads return once it passes. Store any image you need for longer while the message is still readable.

The webhook payload

whatsapp.received carries the image arm on the event envelope, media reference and all:
代码示例
{
  "type": "whatsapp.received",
  "timestamp": "2026-08-25T09:04:11.118Z",
  "data": {
    "whatsapp_id": "wam_01kya19eknftrs2s6p82asmvnh",
    "workspace_id": "ws_01ky7m235keycbnwyajabe1a6b",
    "direction": "inbound",
    "from": { "phone_number": "+14155550100" },
    "to": { "phone_number": "+13124495569" },
    "image": {
      "id": "waf_01kyb2m4xq7whs0d8n3prv6tez",
      "url": "https://platform.bird.com/v1/whatsapp/messages/wam_01kya19eknftrs2s6p82asmvnh/media/waf_01kyb2m4xq7whs0d8n3prv6tez",
      "mime_type": "image/jpeg",
      "caption": "Is this the right part?"
    },
    "tags": null,
    "metadata": null
  }
}
The webhook fires when the message arrives, which is also when the retention window starts, so an endpoint that queues the fetch rather than doing it inline has the whole window to catch up.

Things to watch

  • The caption belongs to the contact. It arrives as plain text with no formatting or entity information, so render it as text.
  • One image per message. A contact sending several photos produces several inbound messages, each with its own id and its own media reference. Group them by from and arrival time; no array collects them.

Next steps