Skip to content
Send templates

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.

Terminal window
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"]] }
}
]
}'

A duplicate name + language for the account returns 409 template_already_exists before any Meta call.

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.

ConstraintDetail
Schemehttps:// (public internet; private IPs blocked)
ReachabilityKirimdev servers must be able to GET the URL once at create time
IMAGEJPEG or PNG, ≤ 5 MB (WebP rejected by Meta for samples)
VIDEOMP4 or 3GPP, ≤ 16 MB
DOCUMENTPDF and common office formats, ≤ 100 MB
Content-TypeServed 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".

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.

SituationHTTPerror.code
Invalid body / Meta rejects template content400invalid_field_value
Bad or unreachable header media URL400invalid_field_value
Duplicate name + language409template_already_exists
Meta unreachable / transient upstream fault502whatsapp_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.

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.

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).