Skip to content
Send templates

Send templates

Templates are pre-approved message structures you use to start a conversation, send transactional notifications, or re-engage past customers — anything outside the 24-hour customer service window.

This page covers the simplest case: a body-only template with text parameters. For richer variants — header media, dynamic URL buttons, Flow buttons, named parameters — see the dedicated pages below.

List templates already approved for your WhatsApp account:

Terminal window
curl https://api.kirimdev.com/v1/$PHONE_ID/templates \
-H "Authorization: Bearer $KIRIM_KEY"

Each template returns its name, language, status (approved / pending / rejected), and the parameterised body.

template.name is a Meta identifier, not free text. It may contain only lowercase letters, numbers, and underscores (^[a-z0-9_]+$).

Surrounding whitespace is trimmed for you, so a name pasted with a stray trailing space still sends. Anything trimming cannot repair — interior spaces, uppercase letters, dashes — is rejected up front with 400 invalid_request_error rather than being forwarded to Meta, which answers malformed names with a generic “Something went wrong” that is indistinguishable from a real upstream outage.

"name": "order_shipped" // ok
"name": "order_shipped " // ok - trimmed
"name": "order shipped" // 400
"name": "Order_Shipped" // 400
"name": "order-shipped" // 400
Terminal window
curl -X POST \
https://api.kirimdev.com/v1/$PHONE_ID/messages \
-H "Authorization: Bearer $KIRIM_KEY" \
-H "Content-Type: application/json" \
-d '{
"messaging_product": "whatsapp",
"to": "+628123456789",
"type": "template",
"template": {
"name": "order_shipped",
"language": "id",
"components": [
{
"type": "body",
"parameters": [
{ "type": "text", "text": "Andi" },
{ "type": "text", "text": "INV-4521" }
]
}
]
}
}'

Positional parameters fill the {{1}}, {{2}}, … placeholders in the template body in declaration order.

Templates support three component types. Each takes a parameters array whose shape mirrors Meta’s Cloud API reference verbatim — Kirimdev does not re-shape it.

ComponentPurposeDedicated page
headerMedia or text shown above the bodyHeader media
bodyParameterised text contentThis page
button (per index)Dynamic URL, quick reply, copy codeButtons
button (sub_type: url)OTP / verification code (AUTHENTICATION)OTP
button (sub_type: flow)Trigger a Meta FlowFlow buttons

Parameters can be positional (default) or named — see Named parameters.

To send an approved media card carousel, add a carousel component whose cards array carries one entry per card, keyed by card_index. Each card supplies its header asset (an uploaded media id or a public link) and a parameter for every quick-reply button (payload) and dynamic URL button (text):

{
"messaging_product": "whatsapp",
"to": "+16505551234",
"type": "template",
"template": {
"name": "rare_succulents_carousel",
"language": { "code": "en_US" },
"components": [
{
"type": "carousel",
"cards": [
{ "card_index": 0, "components": [
{ "type": "header", "parameters": [{ "type": "image", "image": { "id": "1558081531584829" } }] },
{ "type": "button", "sub_type": "quick_reply", "index": 0, "parameters": [{ "type": "payload", "payload": "more-aloes" }] }
] },
{ "card_index": 1, "components": [
{ "type": "header", "parameters": [{ "type": "image", "image": { "link": "https://cdn.example.com/echeveria.jpg" } }] },
{ "type": "button", "sub_type": "quick_reply", "index": 0, "parameters": [{ "type": "payload", "payload": "more-echeverias" }] }
] }
]
}
]
}
}

You must send exactly the number of cards the template was approved with. A card header accepts either an uploaded media id (POST /{phone_number_id}/media) or a public link. If a card’s URL button was created dynamic ({{1}} suffix), add a { "type": "button", "sub_type": "url", "index": N, "parameters": [{ "type": "text", "text": "<value>" }] } for it. A static button (static URL / phone number) needs no send-time parameter.

Meta accepts language as either a bare string code ("id", "en_US") or an object { code, policy? }. Kirimdev accepts both - prefer the string form since policy was deprecated by Meta.