Sending photos, invoices and documents over WhatsApp via API
Most WhatsApp API guides stop at plain text. But a lot of the useful automations businesses actually want — invoices, "your car is ready" photos, booking confirmations with an itinerary attached — need a file, not just a sentence. Here's how to send media messages with TextMeFlow, and what to watch for.
The one field you need
TextMeFlow's POST /v1/messages endpoint accepts a normal text message. To turn it into a media message, add a single field: media_url.
curl -X POST https://api.textmeflow.eu/v1/messages \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+32470000000",
"text": "Your invoice for July is attached.",
"media_url": "https://example.com/invoices/2026-07.pdf"
}'
There's no separate upload step and no multipart form data. You host the file somewhere reachable over HTTPS, pass the URL, and TextMeFlow fetches it and delivers it through WhatsApp. The text field becomes the caption shown with the file.
If you're already sending plain-text messages from PHP, Python or Node.js, adding media is a one-line change — see the code examples for those three languages if you haven't wired up the basic call yet.
What renders as what
WhatsApp picks the presentation based on the file's content type, not its extension:
- Images (JPEG, PNG, WebP) — shown inline as a photo, with your text as caption
- Video (MP4) — inline, playable, with caption
- Audio (MP3, OGG) — a voice-note style player
- Everything else (PDF, DOCX, XLSX, ZIP, …) — a document attachment with filename
That last category is the one most business workflows actually use: PDF invoices, signed contracts, spreadsheets, boarding passes. You don't need to pick a "message type" up front — just point at the file and TextMeFlow figures out the rest.
Where to host the file
The URL has to be public over HTTPS and under 2,000 characters. It doesn't have to stay public forever, though. Signed, short-lived URLs work fine and are the recommended approach for anything private:
- S3 or GCS presigned URLs with a short expiry
- Laravel signed routes (
URL::temporarySignedRoute(...)) - Any framework's equivalent of a time-boxed, tamper-proof link
This matters if the file is a customer invoice or a document with personal data in it — you don't want it sitting on a permanently public URL indefinitely. Generate the link right before sending, with an expiry of a few hours, and you're covered.
Limits and quota
Three numbers to keep in mind:
- 100 MB maximum file size
- A media message counts as one message against your monthly quota — same as plain text, no multiplier
- Per-number rate limits still apply unchanged: 1 message/second, 60/minute, 1,000/hour
So attaching a PDF to a confirmation message doesn't cost you anything extra on quota — it's still one send.
Practical patterns worth stealing
A few real setups this maps onto directly:
- Billing systems — generate the PDF invoice, upload it to a bucket, get a presigned URL, call the API. Works the same from a cron job as from a webhook triggered by a payment event.
- Garages — a mechanic snaps a photo of the finished repair on their phone, an internal tool uploads it and fires the API call with
text: "Your car is ready for pickup". - Bookings and hospitality — send the itinerary or door-code instructions as a PDF alongside the confirmation text, instead of making guests dig through email.
- Webshops — attach a product photo to an order-update message so customers can confirm it's the right item before it ships.
All of these also work from no-code tools — the Make.com, n8n or Zapier HTTP module just needs the extra media_url field in the JSON body, no different setup required.
Handling delivery status
Media messages go through the same webhook delivery events as text messages — sent, delivered, read, failed. If you're building anything that reacts to those events (retrying a failed invoice send, for example), make sure you're verifying the webhook signature correctly first; see the guide to verifying HMAC webhook signatures to avoid processing spoofed or duplicate events.
Try it
Full reference and field list live in the media docs. If you want to test this against a real number today, the free plan includes 50 messages a month — enough to wire up an invoice or photo-confirmation flow and see it work before you commit to anything paid.
Zelf WhatsApp-berichten versturen via API?
Gratis voor altijd tot 50 berichten/maand. QR scannen en binnen 5 minuten verstuur je je eerste bericht.
Gratis voor altijd