DEVELOPER GUIDE

Receive WhatsApp media: images, video, voice notes and documents

A reply is not always text. When a customer sends a photo, a voice note or a PDF, message.received tells you what kind of thing arrived and hands you everything you need to fetch it.

Every reply carries a kind

message.received always includes kind: one of text, image, video, audio or document. When a file came with the message, a media object describes it. body is the caption — and an empty caption is normal, not missing, because most people send a photo with no words.

The media object

FieldTypeNotes
kindstringimage, video, audio or document.
mimetypestringThe stored content type, e.g. image/jpeg.
filenamestring | nullPresent for documents; usually null for photos and voice notes.
byte_sizeinteger | nullNull when the transport did not report a size.
duration_secondsinteger | nullSeconds, for audio and video.
voicebooleanTrue for a WhatsApp voice note, false for a forwarded audio file.
download_urlstring | nullWhere the bytes are fetched. Null when the file cannot be retrieved.

The voice flag is the one that saves you a guess: a voice note and an audio file forwarded from someone's music both arrive as audio, and only this tells them apart.

An image with a caption

POST your endpoint — message.received
{
  "id": "whd_9f2c81a0e34b4d1e8c5a7b93f0d2e611",
  "type": "message.received",
  "created_at": "2026-09-22T09:14:03.118Z",
  "workspace_id": "019bf4a1-0000-7000-8000-000000000000",
  "data": {
    "message_id": "019bf4c3-0000-7000-8000-000000000001",
    "connection": "main",
    "from": { "phone": "+15551234567" },
    "body": "Is this the right unit?",
    "kind": "image",
    "media": {
      "kind": "image",
      "mimetype": "image/jpeg",
      "filename": null,
      "byte_size": 214883,
      "duration_seconds": null,
      "voice": false,
      "download_url": "/v1/messages/019bf4c3-0000-7000-8000-000000000001/media"
    },
    "received_at": "2026-09-22T09:14:02.740Z"
  }
}

A voice note

POST your endpoint — message.received
{
  "data": {
    "message_id": "019bf4c3-0000-7000-8000-000000000002",
    "connection": "main",
    "from": { "phone": "+15551234567" },
    "body": "",
    "kind": "audio",
    "media": {
      "kind": "audio",
      "mimetype": "audio/ogg; codecs=opus",
      "filename": null,
      "byte_size": 18244,
      "duration_seconds": 7,
      "voice": true,
      "download_url": "/v1/messages/019bf4c3-0000-7000-8000-000000000002/media"
    }
  }
}

Nothing inbound is rejected

Classification on receipt describes the file; it never refuses it. A sticker arrives as an image, and any type ChatRail cannot pin down is treated as a document — the same thing WhatsApp does with an arbitrary file. The message has already been sent by a real person, so losing it to a strict content check would lose a real reply.

A file the transport will not hand over still arrives, with download_url set to null. “A voice note arrived and we cannot fetch it” is worth knowing; dropping the event would lose the reply along with the file. Fetch the bytes with the download guide.

Related documentation

API reference · Webhook events · WhatsApp API · Security

BUILD IT

Wire it against the real schemas.

Every shape on this page is the one the endpoint validates. Verify it in your own stack before production rollout.

Gain Access