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.

File vs URL — pick deliberately A DOCUMENT / IMAGE / VIDEO header delivers a native WhatsApp media bubble. A URL inside the body text shows a clickable link. These are different template designs and different API fields.

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

  1. Create and approve a UTILITY (or other allowed) template in the dashboard with header format DOCUMENT. Upload any valid sample PDF for Meta review.
  2. At send time, pass a public HTTPS URL to the customer-specific file via header_media_url, or upload the bytes via multipart header_media_file.
  3. Washeej forwards the template to Meta with a document header parameter.
  4. 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.
The URL is for Meta, not for the customer 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 GET with the binary body (query-string download endpoints are fine if they return the file).
  • Preferred Content-Type: application/pdf for 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-Disposition filename ending in .pdf so 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, code 131053 "did not respond back in time"). Generate the PDF before you call the API and serve it as a static file with a full Content-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.
What Washeej does with the URL Washeej does not download, cache or re-host your file. Before sending it makes one 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.
HTTP 200 means accepted, not delivered A successful send response means Meta accepted the message. Media download and delivery happen afterwards; failures arrive as 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 — API v1
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 are optional Include 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 — multipart upload
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, …).

  1. Header format: DOCUMENT (sample PDF for Meta review).
  2. Body example: Hello {{1}}, invoice {{2}} is attached. Amount due: {{3}} {{4}}.
  3. Send with header_media_url (or file upload) and ordered body_variables.
Meta variable formatting Variables cannot be the first or last characters of the body/header text. Keep static words before and after each placeholder.

7. Scenario D — IMAGE or VIDEO headers

Same API fields as documents. Only the approved template header_format changes.

JSON — image header
{
  "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

  1. Call GET /templates (scope templates:read).
  2. Use either the Washeej numeric id or Meta whatsapp_template_id as template_id when sending.
  3. Confirm header_format is DOCUMENT, IMAGE, or VIDEO before relying on header_media_url.
  4. 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 — legacy /external-api
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