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).

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://staging-mobile.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://staging-mobile.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://staging-mobile.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
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