Media

Any media field takes one of three forms, and Njiwa works out which.

Starts with http:// or https:// Njiwa fetches it server side.
Starts with med_ A file you already uploaded to the media library.
Starts with data: A base64 data URI, decoded in place.

Or skip all three and send the file itself as multipart form data.

Upload once, send many times

curl -X POST https://njiwa.upeo.ai/v1/media \
  -H "Authorization: Bearer sk_live_..." \
  -F file=@price-list.pdf
{ "id": "med_bw5iuma3pvfc7d53bnxvakbx2y", "filename": "price-list.pdf",
  "mime": "application/pdf", "size": 184320, "sha256": "..." }

Then send that id to as many people as you like. Njiwa caches the WhatsApp side upload against the file, so a 5,000 recipient campaign uploads it once rather than 5,000 times.

Limits

Images5 MB
Video16 MB
Audio16 MB
Documents100 MB
Stickers100 KB static, 500 KB animated

These mirror the WhatsApp Cloud API so one contract holds on both rails. Going over returns media_too_large naming your actual size and the limit.

PDFs

Njiwa sniffs the magic bytes rather than trusting the declared content type, because half of all generated invoices arrive as application/octet-stream. If the bytes say PDF, it is sent as a PDF.

Always send a filename. A document with no filename renders as an unnamed blob and looks broken to the recipient. If you omit it, Njiwa derives one.

Fetching your URLs

What Njiwa will not fetch. Loopback, private network, link-local and cloud metadata addresses are all refused. DNS is resolved once and that address is pinned for the connection, redirects are capped at three and revalidated, and both size and wall clock time are capped. This closes the server side request forgery hole that a URL fetching endpoint otherwise is.

Downloading

Media reaches you as a signed, expiring Njiwa URL, never as a raw WhatsApp reference you cannot use. Links last 24 hours; fetch a fresh one from GET /v1/media/{id}.