Create templates
Use POST /v1/{phone_number_id}/templates to author a template
programmatically. On success Kirimdev persists the row with
status: "pending" and forwards the structure to Meta for review.
Poll GET /v1/{phone_number_id}/templates/{name} (or run
POST .../templates/sync) until Meta sets approved or
rejected.
This page covers creation. To send an already-approved template (including runtime header images), see Send templates and Header media at send time.
Body-only template
Section titled “Body-only template”curl -X POST "https://api.kirimdev.com/v1/$PHONE_ID/templates" \ -H "Authorization: Bearer $KIRIM_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "order_update", "category": "UTILITY", "language": "id", "components": [ { "type": "BODY", "text": "Pesanan {{1}} sedang diproses.", "example": { "body_text": [["A123"]] } } ] }'import { Kirim } from '@kirimdev/sdk'
const kirim = new Kirim({ apiKey: process.env.KIRIM_KEY! })
const template = await kirim .phoneNumbers(process.env.PHONE_ID!) .templates.create({ name: 'order_update', category: 'UTILITY', language: 'id', components: [ { type: 'BODY', text: 'Pesanan {{1}} sedang diproses.', example: { body_text: [['A123']] }, }, ], })
console.log(template.status) // "pending"A duplicate name + language for the account returns 409 template_already_exists before any Meta call.
Media header (IMAGE / VIDEO / DOCUMENT)
Section titled “Media header (IMAGE / VIDEO / DOCUMENT)”Meta requires a sample asset at creation time. Kirimdev accepts a
public https:// URL, downloads the file server-side, uploads it to
Meta via the Resumable Upload API, and submits the resulting asset
handle to Meta on your behalf. You only need your Kirim API key — no
direct Meta token required.
Preferred field: example.header_url
{ "name": "promo_gambar", "category": "MARKETING", "language": "id", "components": [ { "type": "HEADER", "format": "IMAGE", "example": { "header_url": ["https://cdn.example.com/banners/juni.jpg"] } }, { "type": "BODY", "text": "Halo {{1}}, ada promo spesial hari ini.", "example": { "body_text": [["Budi"]] } } ]}Backward-compatible alias: you may place the same public URL in
example.header_handle instead of header_url. Kirimdev detects
https:// values and treats them as URLs to fetch.
Requirements for sample URLs
Section titled “Requirements for sample URLs”| Constraint | Detail |
|---|---|
| Scheme | https:// (public internet; private IPs blocked) |
| Reachability | Kirimdev servers must be able to GET the URL once at create time |
| IMAGE | JPEG or PNG, ≤ 5 MB (WebP rejected by Meta for samples) |
| VIDEO | MP4 or 3GPP, ≤ 16 MB |
| DOCUMENT | PDF and common office formats, ≤ 100 MB |
| Content-Type | Served by the origin, or inferable from the file extension |
If the URL is unreachable, the wrong type, or too large, the API
returns 400 invalid_field_value with param: "components".
Already have a Meta handle?
Section titled “Already have a Meta handle?”If you uploaded the asset yourself via Meta’s Resumable Upload API, pass the handle verbatim:
"example": { "header_handle": ["4::aW1hZ2UvanBlZw==:YOUR_HANDLE"] }Kirimdev passes it through without re-uploading.
Errors
Section titled “Errors”| Situation | HTTP | error.code |
|---|---|---|
| Invalid body / Meta rejects template content | 400 | invalid_field_value |
| Bad or unreachable header media URL | 400 | invalid_field_value |
| Duplicate name + language | 409 | template_already_exists |
| Meta unreachable / transient upstream fault | 502 | whatsapp_upstream_error |
Meta validation failures (wrong examples, format rules) surface as
400, not 502 — retrying the same payload will not succeed until the
components are fixed.
Dashboard alternative
Section titled “Dashboard alternative”The Kirimdev dashboard can also create templates and accepts a media
library file via headerMediaId on the internal API. The Public API
does not expose headerMediaId; use example.header_url or create from
the dashboard UI instead.
Authentication (OTP) templates
Section titled “Authentication (OTP) templates”Category AUTHENTICATION is for one-time passwords. Meta auto-approves
these templates and generates the body text; you configure the security
disclaimer, optional expiry footer, and OTP button. Full create + send
examples live on OTP (authentication templates).