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"
}| Field | What it carries |
|---|---|
| id | The stored file, to pass as media_id when fetching the bytes |
| url | A Bird URL, fetched with your API key |
| mime_type | The media type WhatsApp reported for the file, such as image/jpeg |
| caption | The 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
- How receiving works: the inbound envelope, media fetching, and the whatsapp.received webhook
- WhatsApp image messages: the send side of the same arm
- Receiving documents: the same media shape, with the sender's filename
- WhatsApp events: the full event list, over the API or webhooks