SigFollow
Sign in

API reference

Template media

Upload media for a WhatsApp template's header component and get back a Meta header_handle. SigFollow wraps Meta's multi-step Resumable Upload API so callers only need a regular API key — App-level Meta credentials stay on the platform side.

Upload header media

POST/v1/:wabaId/template_media

Scope: templates:write

Path parameters

NameTypeRequiredDescription
wabaIdstringyesWhatsApp Business Account ID. The upload handle is scoped to this WABA and only valid for templates owned by it.

Request

Body must be multipart/form-data with a single field:

NameTypeRequiredDescription
filebinaryyesThe media file. MIME must be in the template-header whitelist(image / video / document — sticker and audio are NOT acceptable as a template header) and the file's magic bytes must match the declared Content-Type.
Two safety layers
  1. MIME whitelist — only template-header-eligible types are accepted (see below). Sticker / audio / generic types are rejected at the edge so they never reach Meta.
  2. Magic-byte verification — the first bytes of the upload must match the declared MIME, so HTML disguised as image/jpegcan't produce a Meta handle that renders as a script when sent.

Accepted MIME types

  • Image: image/jpeg, image/png
  • Video: video/mp4, video/3gpp
  • Document: application/pdf, text/plain, .docx, .xlsx, .pptx, plus legacy application/vnd.ms-excel

Note that this is stricter than the whitelist on POST /v1/:phoneNumberId/media (no sticker / audio) — Meta accepts those in messaging but not as template headers, so we fail the request early instead of letting it reach template review.

File size cap: 16 MiB
Uploads larger than 16 MiB are rejected with 413 Payload Too Large. This matches the cap on the messaging media endpoint and the Meta Resumable Upload Single-shot path. Larger media should be split or referenced by URL.

Example

curl -X POST https://api.sigfollow.com/v1/WABA_ID/template_media \
  -H "Authorization: Bearer sflo_live_xxx" \
  -F "file=@/path/to/banner.jpg;type=image/jpeg"

Response

{ "header_handle": "4::aW1hZ2UvanBlZw==:ARZ..." }
Response fields
NameTypeRequiredDescription
header_handlestringnoMeta upload handle, form 4::aW1hZ2Uv...:ARZ... (sometimes prefixed h:). Pass it to the components[].example.header_handle array when creating a template via POST /v1/:wabaId/message_templates. The handle is only valid at template-creation time — it cannot be reused for sending messages.

End-to-end flow

A typical "create an IMAGE template from scratch" call sequence:

  1. POST /v1/:wabaId/template_media with the file — receive header_handle.
  2. POST /v1/:wabaId/message_templates with a HEADER component whose example.header_handle: ["<handle>"] embeds the value from step 1. See templates Recipe #1 for the full body shape.
  3. Wait for Meta approval (PENDING → APPROVED / REJECTED webhooks).
  4. Send via POST /v1/:phoneNumberId/messages with type: "template". At send time the header parameter takes a normal media_id / linknot the header_handle.

Errors

  • 400 — MIME outside the template-header whitelist or magic-byte mismatch
  • 401 code 190 — invalid / revoked API key
  • 403 code 200 — missing templates:writescope or WABA does not belong to the API key's tenant
  • 413 — file exceeds 16 MiB
  • 429 code 4 — rate limit exceeded
  • 500 code 131000 — upstream Meta Resumable Upload failure (network / token / etc.); SigFollow does not retry internally

See the error reference for body schema.