Receiving WhatsApp audio
Audio a contact sends arrives as an inbound message carrying audio. The arm covers both a voice note they recorded in the chat and an audio file they attached, and voice is what tells the two apart.
What an inbound audio carries
Code example
{
"id": "wam_01kyb3q8ndvt6r2h5k9wxc4pfa",
"direction": "inbound",
"from": { "phone_number": "+14155550100" },
"to": { "phone_number": "+13124495569" },
"status": "received",
"audio": {
"id": "waf_01kyb2m4xq7whs0d8n3prv6tez",
"url": "https://platform.bird.com/v1/whatsapp/messages/wam_01kyb3q8ndvt6r2h5k9wxc4pfa/media/waf_01kyb2m4xq7whs0d8n3prv6tez",
"mime_type": "audio/ogg; codecs=opus",
"voice": true
},
"created_at": "2026-08-25T09:07:32Z"
}| 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, such as audio/ogg; codecs=opus for a voice note |
| voice | true for a voice note recorded in the chat, rather than an attached audio file |
caption does not exist on this arm in either direction, so an audio message never carries text alongside it. A contact who wants to add words sends a separate text message.
A voice note auto-downloads in the WhatsApp client and can be transcribed there for the recipient. Bird passes the audio through and does not transcribe it, so the transcript your contact sees in their own app is not part of the message you read.
Fetching the bytes
Pass the message ID and the media id to the channel's media method. The hub's fetching inbound media carries that call in every language, along with the redirect and header rules it follows.
mime_type names the container and the codec, and a voice note is Opus in an Ogg container rather than the MP3 or AAC an attachment is likely to be. Read the type off the field rather than off the file extension in url, which carries none, and check it before handing the bytes to a transcription service that accepts only some formats. 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.
The webhook payload
whatsapp.received carries the audio arm on the event envelope, voice flag included:
Code example
{
"type": "whatsapp.received",
"timestamp": "2026-08-25T09:07:32.451Z",
"data": {
"whatsapp_id": "wam_01kyb3q8ndvt6r2h5k9wxc4pfa",
"workspace_id": "ws_01ky7m235keycbnwyajabe1a6b",
"direction": "inbound",
"from": { "phone_number": "+14155550100" },
"to": { "phone_number": "+13124495569" },
"audio": {
"id": "waf_01kyb2m4xq7whs0d8n3prv6tez",
"url": "https://platform.bird.com/v1/whatsapp/messages/wam_01kyb3q8ndvt6r2h5k9wxc4pfa/media/waf_01kyb2m4xq7whs0d8n3prv6tez",
"mime_type": "audio/ogg; codecs=opus",
"voice": true
},
"tags": null,
"metadata": null
}
}An automated flow that answers voice notes reads voice here, before fetching anything: a support queue that transcribes recordings but forwards attached music files can branch on the flag alone.
Things to watch
- Read voice as a flag that marks a voice note. Anything else, an absent field included, is an ordinary audio attachment.
- No duration is reported. The arm carries no length, so read it from the file after fetching if a queue needs it.
Next steps
- How receiving works: the inbound envelope, media fetching, and the whatsapp.received webhook
- WhatsApp audio messages: the send side of the same arm
- Receiving video: clips, with a caption the audio arm has no room for
- WhatsApp events: the full event list, over the API or webhooks