Why media changes the outcome of a conversation
An invoice as a PDF gets more done than a link to it. A product photo sells better than a description. A short voice note from a sales rep feels closer than three paragraphs. In business systems, the receipt a customer photographs and sends back needs to reach the finance team without anyone downloading and uploading files by hand.
A WhatsApp API handles both directions: your system attaches files to the messages it sends and receives the files customers send, ready to store or process.
Sending endpoints by media type
| Type | Endpoint | File field | Useful options |
|---|---|---|---|
| Image | /messages/send/image | image | caption |
| Audio | /messages/send/audio | audio | ptt (voice note) |
| Video | /messages/send/video | video | caption, ptv, gifPlayback |
| Document | /messages/send/document | document | fileName, mimetype |
| Album | /messages/send/album | media (list) | caption per item |
| Sticker | /messages/send/sticker | sticker | - |
Every route lives under https://api.d-api.cloud/api/v1 and requires sessionId and to, with the number in international format without the plus sign, like 14155550123.
Sending examples
A document with a friendly name
curl -X POST https://api.d-api.cloud/api/v1/messages/send/document \
-H "Authorization: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"sessionId": "billing",
"to": "14155550123",
"document": "https://files.yourcompany.com/invoices/8812.pdf",
"fileName": "invoice-september.pdf",
"mimetype": "application/pdf"
}'Without fileName, the customer sees a generic name and gets suspicious. It is a small detail that cuts down on "is this a scam?" questions in support.
Image, voice note and album with the Node SDK
import { DApi } from 'd-api-sdk'
const dapi = new DApi({ apiKey: process.env.DAPI_KEY })
await dapi.messages.sendImage({
sessionId: 'store',
to: '14155550123',
image: 'https://cdn.yourcompany.com/products/blue-sneakers.jpg',
caption: 'Back in stock in your size. Want me to hold a pair?',
})
await dapi.messages.sendAudio({
sessionId: 'store',
to: '14155550123',
audio: 'https://cdn.yourcompany.com/audio/welcome.ogg',
ptt: true,
})
await dapi.messages.sendAlbum({
sessionId: 'store',
to: '14155550123',
media: [
{ type: 'image', url: 'https://cdn.yourcompany.com/products/front.jpg', caption: 'Front' },
{ type: 'image', url: 'https://cdn.yourcompany.com/products/side.jpg', caption: 'Side' },
],
})The SDK is a thin client over the same REST API, so everything above also works with any HTTP client. More details on the Node.js SDK page.
Public URL or base64: how to decide
For single-file sends, the file field accepts two formats, and each has its place. In albums, items always go by URL.
- Public URL: the file lives in storage or on a CDN and you pass the link. The request stays light, the same file serves many recipients and you are not pushing megabytes on every call. The link must respond without a login and without redirecting to an authentication page.
- Base64: the content goes inside the JSON. It makes sense for files generated on the fly, like a receipt built by your system, or when the document holds sensitive data and you do not want it behind an accessible link. The cost is a larger payload.
If you go with a URL for sensitive data, use signed links with a short expiry. The API only needs to fetch the file at the moment of sending.
Receiving and downloading media customers send
When a customer sends a photo, audio or document, the messages.received event reaches your webhook with type set to image, audio, video, document or sticker. The media_url field usually already carries a link to the stored file, and media_data carries type, size, caption, duration and, for audio, whether it is a voice note.
If you need the content as base64 (to send to OCR or a transcription model, for example) or the link came back empty, use the download endpoint. WhatsApp delivers media encrypted, and the endpoint decrypts it using the metadata that came with the event:
curl -X POST https://api.d-api.cloud/api/v1/media/download \
-H "Authorization: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"sessionId": "billing",
"direct_path": "<from the webhook>",
"media_key": "<from the webhook>",
"mimetype": "image/jpeg",
"base64": true
}'Without base64: true, the file is saved to storage and the response returns media_url. For large files, the async parameter runs the download in the background. If you want to keep everything in your own bucket, you can use the native S3 or MinIO integration.
Common mistakes when sending media
- A link that requires login or expires too fast: the send fails or arrives empty. Test the URL in a private browser window.
- Audio without
pttwhen it should feel like a voice note: the customer gets a file, not a message. - Sending the same heavy media in bulk to lots of people: besides the bandwidth cost, unusual sending volume is one of the signals that lead to bans. Read how to avoid bans before automating campaigns.
- Processing media before answering the webhook: download and handle the file in a worker, after returning 200.
If you want to send media from triggers in your system without writing code, see the WhatsApp API for automation. To combine media with clickable options, continue to buttons and lists.
