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_mediaScope: templates:write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| wabaId | string | yes | WhatsApp 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:
| Name | Type | Required | Description |
|---|---|---|---|
| file | binary | yes | The 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
- 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.
- 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 legacyapplication/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
| Name | Type | Required | Description |
|---|---|---|---|
| header_handle | string | no | Meta 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:
POST /v1/:wabaId/template_mediawith the file — receiveheader_handle.POST /v1/:wabaId/message_templateswith a HEADER component whoseexample.header_handle: ["<handle>"]embeds the value from step 1. See templates Recipe #1 for the full body shape.- Wait for Meta approval (PENDING → APPROVED / REJECTED webhooks).
- Send via
POST /v1/:phoneNumberId/messageswithtype: "template". At send time the header parameter takes a normalmedia_id/link— not theheader_handle.
Errors
400— MIME outside the template-header whitelist or magic-byte mismatch401code190— invalid / revoked API key403code200— missingtemplates:writescope or WABA does not belong to the API key's tenant413— file exceeds 16 MiB429code4— rate limit exceeded500code131000— upstream Meta Resumable Upload failure (network / token / etc.); SigFollow does not retry internally
See the error reference for body schema.