Guides
Template Media Headers
How to send approved Cloud templates that carry a native WhatsApp document (PDF), image, or video — including per-customer files from your ERP or storage — without putting a download URL in the message text.
1. Choose the right pattern
| Customer should receive | Template header | API field at send time | Typical use |
|---|---|---|---|
| Native PDF / document bubble | DOCUMENT |
header_media_url or header_media_file |
Invoices, contracts, statements, tickets |
| Native image | IMAGE |
header_media_url or header_media_file |
Receipts, banners, product shots |
| Native video | VIDEO |
header_media_url or header_media_file |
Short explainers, demos |
| Clickable URL in the text | None / text header | body_variables containing the URL |
Hosted download pages — see Dynamic Invoice Link |
| Same sample media every time | DOCUMENT / IMAGE / VIDEO |
Omit media fields (uses template sample) | Static announcements only |
2. How DOCUMENT delivery works
- Create and approve a UTILITY (or other allowed) template in the dashboard with header format DOCUMENT. Upload any valid sample PDF for Meta review.
- At send time, pass a public HTTPS URL to the customer-specific file via
header_media_url, or upload the bytes via multipartheader_media_file. - Washeej forwards the template to Meta with a document header parameter.
- Meta downloads the file from that URL (server-to-server) and delivers a WhatsApp document. The end user sees a file, not the raw URL.
header_media_url is a fetch address for Meta's servers. Do not confuse it with putting a link inside body_variables.
3. Media URL requirements
- Must be HTTPS and reachable from the public internet (not
localhost, not private VPC-only hosts). - Must return HTTP 200 on
GETwith the binary body (query-string download endpoints are fine if they return the file). - Preferred
Content-Type:application/pdffor documents, matching image/video MIME for other headers. - Meta document size limit: 100 MB (images 5 MB, videos 16 MB).
- Prefer a path or
Content-Dispositionfilename ending in.pdfso WhatsApp shows a clear file name. - Signed / short-lived URLs are OK if they remain valid long enough for Meta to fetch at send time.
- Auth walls, HTML login pages, or HTML error bodies will fail Meta media download (often surfaced as media upload errors).
- Serve a ready file, fast. Meta downloads the URL itself, shortly after the send, and gives up if your server is slow (webhook
message.failed, code131053"did not respond back in time"). Generate the PDF before you call the API and serve it as a static file with a fullContent-Length; do not build it inside the download request. - Use a unique URL per file (for example a token in the path) so a fetch never reuses an older file.
HEAD request (User-Agent Washeej-MediaCheck/1.0, 5 s timeout, redirects not followed) only to read Content-Length and reject files over Meta’s limit; an unanswered HEAD never blocks the send. Links to private, loopback or reserved addresses are rejected. Meta then fetches the file from your server.
message.failed webhooks with Meta’s code.
4. Scenario A — Dynamic PDF via public URL (JSON)
Best when your system already stores each document behind a stable or signed HTTPS link.
curl -X POST "https://app.washeej.com/v1/templates/send" \
-H "client-id: YOUR_CLIENT_ID" \
-H "client-secret: YOUR_CLIENT_SECRET" \
-H "Content-Type: application/json" \
-d '{
"template_id": "YOUR_TEMPLATE_ID",
"mobile_code": "966",
"mobile": "500000000",
"whatsapp_account_id": YOUR_CLOUD_ACCOUNT_ID,
"header_media_url": "https://files.example.com/docs/INV-1001.pdf",
"body_variables": ["Ahmed", "INV-1001"]
}'
body_variables only if the approved template body contains {{1}}, {{2}}, … Placeholders. A fully static body needs no body variables.
5. Scenario B — Upload the PDF at send time (multipart)
Best when the file lives only on your app server and you do not want a public URL.
curl -X POST "https://app.washeej.com/v1/templates/send" \
-H "client-id: YOUR_CLIENT_ID" \
-H "client-secret: YOUR_CLIENT_SECRET" \
-F "template_id=YOUR_TEMPLATE_ID" \
-F "mobile_code=966" \
-F "mobile=500000000" \
-F "whatsapp_account_id=YOUR_CLOUD_ACCOUNT_ID" \
-F "header_media_file=@/path/to/invoice.pdf;type=application/pdf" \
-F "body_variables[]=Ahmed" \
-F "body_variables[]=INV-1001"
Accepted file fields: header_media_file or media_file. Washeej stores the upload temporarily and passes a public fetch URL to Meta.
6. Scenario C — DOCUMENT header + dynamic body text
Use when the customer should get both a PDF bubble and personalized text (name, invoice number, amount, …).
- Header format: DOCUMENT (sample PDF for Meta review).
- Body example:
Hello {{1}}, invoice {{2}} is attached. Amount due: {{3}} {{4}}. - Send with
header_media_url(or file upload) and orderedbody_variables.
7. Scenario D — IMAGE or VIDEO headers
Same API fields as documents. Only the approved template header_format changes.
{
"template_id": "YOUR_TEMPLATE_ID",
"mobile_code": "966",
"mobile": "500000000",
"whatsapp_account_id": YOUR_CLOUD_ACCOUNT_ID,
"header_media_url": "https://cdn.example.com/receipts/1001.jpg",
"body_variables": ["Order 1001"]
}
- IMAGE — JPEG/PNG, max 5 MB.
- VIDEO — MP4, max 16 MB.
- DOCUMENT — PDF preferred, max 100 MB.
8. Scenario E — URL in the message text (not a file)
If the product requirement is a link inside the chat text, do not use a DOCUMENT header.
Create a text template and pass the URL in body_variables.
Full walkthrough:
Dynamic Invoice Link.
9. Scenario F — Free-form document inside the 24-hour window
Within 24 hours of the customer’s last inbound message you may send a regular document with
POST /messages
(session message). Outside that window, WhatsApp requires an approved template —
use this guide’s DOCUMENT header pattern.
10. Discover template IDs and header format
- Call
GET /templates(scopetemplates:read). - Use either the Washeej numeric
idor Metawhatsapp_template_idastemplate_idwhen sending. - Confirm
header_formatisDOCUMENT,IMAGE, orVIDEObefore relying onheader_media_url. - Send only from a Cloud API channel (
whatsapp_account_id). Device/QR channels do not support Meta templates.
11. Legacy Cloud API
The same fields work on the legacy path (existing integrations):
curl -X POST "https://app.washeej.com/external-api/inbox/send-template-message" \
-H "client-id: YOUR_CLIENT_ID" \
-H "client-secret: YOUR_CLIENT_SECRET" \
-H "Content-Type: application/json" \
-d '{
"template_id": "YOUR_TEMPLATE_ID",
"mobile_code": "966",
"mobile": "500000000",
"whatsapp_account_id": YOUR_CLOUD_ACCOUNT_ID,
"header_media_url": "https://files.example.com/docs/INV-1001.pdf"
}'
New integrations should prefer POST /v1/templates/send.
12. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Customer sees a URL in text, not a file | URL was put in body_variables on a text template |
Use a DOCUMENT-header template + header_media_url |
| Media / upload errors from Meta | URL not publicly downloadable, HTML error page, wrong MIME, or oversized file | Verify public GET returns binary PDF/image/video under Meta limits |
message.failed code 131053: "did not respond back in time" |
Your server answered Meta's download too slowly (often a PDF generated inside the download request) | Pre-generate the file and serve it statically with Content-Length; send again |
| Error: "Template header media URL must be a public http(s) link." | The link points at a private, loopback or reserved address, or uses another scheme | Use a public https:// URL reachable by Meta |
| Same sample PDF every time | header_media_url / file omitted |
Pass the customer-specific media on every send |
capability_error |
Sending from Device/QR channel | Use a Cloud account whatsapp_account_id |
not_found for template |
Wrong id / not approved / wrong account | Re-check GET /templates for that Cloud number |
Validation error on header_media_url |
Not a valid absolute URL | Use full https://… value |