Media assets end to end: upload, tag, and use in a template
The Media Library page lists what your workspace has; this cookbook is the end-to-end loop operators actually run: upload the asset, hold onto a reference that survives, and point a template or send at it. Worked examples cover the three places media reaches a customer — a WhatsApp template header, an MMS payload, and a played-audio voice prompt.Upload and delete require the
files:write scope on your API key; list
and presign read under files:read. Console access is gated to owner,
admin, and developer roles — the same gate the server applies.1. What lives in the Media Library
Every upload surface writes to one tenant-owned storage namespace: images dropped into the email Builder, message-composer attachments, recorded greetings, and porting documents. The file table shows thefile_* id,
filename, MIME type, size, and upload timestamp, and the Files API lists
the same rows.
Channel-safe MIME classes:
- Images — JPEG, PNG, GIF, WebP (WhatsApp headers, MMS image, email blocks)
- Audio — MP3, OGG, WAV, MP4-audio (voice prompts played to callers)
- Video — MP4, 3GPP (WhatsApp video headers, MMS video)
- Documents and text — PDF, DOC/DOCX, XLS/XLSX, plain text, CSV
Content-Type with a 422 — so a renamed executable cannot
reach your library dressed as an image.
2. Uploading assets
Choose the API for anything you reference from code, and the console (Settings → Media) when a human is picking the file.In the console
Open Settings → Media and use the upload action to drop the file into the tenant bucket. The row it creates carries the file id and a fresh signed link — everything you need for step 3.Programmatically
POST /api/v1/files/upload accepts one multipart/form-data field named
file:
cURL
file_* id and a signed url:
Result
ttl_ms stretches it up to the 7-day signing
ceiling (604800000 ms). The rule for templates: if the URL will be
stored — a saved email Builder block, an MMS send request, a played-audio
verb — pass ttl_ms on upload, and prefer passing the file_* id
wherever the channel accepts it, since a stored signed URL always has a
deadline either way. The errors to expect: 422 for an unsupported
MIME class or a content sniff mismatch, 413 when the file exceeds
25 MB, and 404 when a presign requests a file id outside your tenant
namespace.
3. Attaching media to templates
Three attach shapes, one asset. Tag point: label the file consistently at upload (product-photo.png, welcome-greeting.wav) — filename and MIME
type are the two identifiers the console filter searches, so naming is
your tagging scheme.
WhatsApp template header image
A WhatsAppHEADER component with format: "IMAGE" (or "VIDEO") puts
your uploaded asset at the top of every message rendered from the
template:
Template component
components array), then send
it by template_name. Meta reviews the header media alongside the body
during approval, so upload clean — carrier-safe classes from section 1 —
and the handle you pin is the file_* id the upload response returned,
which survives far longer than any signed URL would.
MMS attachment
Pass the media URL on the plain message send:cURL
body, template_name, or media_url is required. MMS
accepts one attachment on this surface; the media-conversion endpoint
(POST /messages/media/convert) re-encodes HEIC/BMP/TIFF or oversized
images down to a carrier-native format before you point the send at them.
Voice prompt
Reference the uploaded audio in aplay verb — either in staticVerbs
on the create call or served from your answer_url when the call picks
up:
Play verb
ttl_ms, or re-sign via the presign endpoint
before placing calls in bulk. The
programmable voice guide walks both
control modes.
4. Deletion, retention, and deduplication
DELETE /api/v1/files/{id} is permanent → returns 204, and there is
no usage check: the platform does not stop you deleting a file a template
still references, because draft-template references are yours to manage.
Search templates for the file id or signed URL before confirming.
Retention is tenant-owned: the
Retention console applies per-channel
media windows (voice recordings, SMS/MMS, email, WhatsApp, RCS, Viber)
with a 7–3650-day clamp, and a lifecycle safety net hard-purges media at
365 days regardless. Deduplication is naming discipline: there is no
automatic content-hash dedupe — the same bytes uploaded twice land twice
— so pin one canonical file id per asset and reuse it across channels.
5. Efficient patterns
- Store file ids, not signed URLs. A
file_*id is durable; a signed URL never lasts past its signing window. Re-mint withGET /api/v1/files/{id}/presign?ttl_ms=…(optional TTL clamped between 1 minute and 7 days; omit it for the 1-hour default). - Set
ttl_msat upload for long-lived rows. Email Builder image blocks and archived media pinned without an explicit TTL breaking silently at the 1-hour default is the most common failure this library sees. - Respect per-channel limits at the source. MMS carriers cap media size below the 25 MB upload ceiling, and WhatsApp image headers have their own Meta limits — the carrier, not the library, rejects an oversized attachment at send time.
- CDN and caching. Signed URLs ride the Google Cloud Storage edge; you cannot cache the URL pair, but you can cache the file id and re-sign on demand, which is cheaper than re-uploading.
6. Webhooks and moderation callbacks
The upload path validates synchronously — MIME allow-list, magic-byte sniff, size cap — so there are no asynchronous moderation callbacks to subscribe to and no file-upload webhook events. A rejected upload answers 422 inline with the failing class (Unsupported file type, content does not match declared type), which keeps your template-attach loop
atomic: either the asset is safe to reference, or the response tells you
why not before any template points at it.
See also
- Media Library console — browse, re-mint, and delete from Settings → Media.
- Files API reference — full upload, list, presign, and delete schemas.
- Templates API — attach a header component
and reference it by
template_name. - Retention console — the per-channel media windows that govern stored files.