Templates
Pre-approved WhatsApp copy. Required for every out-of-session message on production. Submitted to Meta, approved asynchronously, then usable by name.
Lifecycle
draft → submitted → approved (sendable)
↓
rejected (edit + resubmit)
↓
paused | disabledCreate a draft
Template language is a string WhatsApp language code such as pt_BR or en_US. Do not send Meta's nested {"code":"pt_BR"} object shape.
curl https://api.tyxter.com/v1/templates \
-H "authorization: Bearer $TYXTER_API_KEY" \
-H "content-type: application/json" \
-d '{
"name": "order_update",
"language": "pt_BR",
"category": "utility",
"components": [
{
"type": "BODY",
"text": "Olá {{1}}, seu pedido {{2}} saiu para entrega.",
"example": { "body_text": [["Diego", "ORD-123"]] }
}
]
}'Generate a draft
Returns editable components from a short brief. Tyxter provides the generation provider; customers do not need to configure an LLM route before using this helper. In production each successful generation is a small billable operation (metered as template.generation); sandbox generations are free.
curl https://api.tyxter.com/v1/templates/generate \
-H "authorization: Bearer $TYXTER_API_KEY" \
-H "content-type: application/json" \
-d '{
"description": "Avisar que a conferencia online comeca em 15 minutos",
"language": "pt_BR",
"category": "utility",
"template_type": "text"
}'Submit for approval
Snapshots the current components into a fresh TemplateVersion, transitions the template to submitted, and enqueues the Meta sync worker. Returns 202 — actual Meta approval is async.
Campaign readiness
Before a campaign or broadcast, list templates and verify that the exact requested name/language has an approved version. A targeted send returns template_not_approved when the template is missing, still draft, rejected, paused, or disabled.
If no approved template exists, complete the create → submit → approval lifecycle first. Do not silently switch to another template or language.
Edit a draft
Draft and rejected templates can be edited. Image, document, and video headers must includeexample.header_handle[0], a Meta media handle used for review. Body and text header placeholders must include matching sample values in example.body_text orexample.header_text.
Duplicate a template
Duplicating creates a new draft with copied components and no provider template id. Supply a new name when you want a predictable slug, or omit it to let Tyxter choose the next available_copy name.
Authoring and quality signals
Template responses include provider_quality andauthoring_signals. Provider quality is derived from the durable Meta lifecycle state. Authoring signals warn about likely category mismatches, such as promotional language inside a utility template, before you submit for review.
Approval webhooks
template.approvedfires when Meta accepts.template.rejectedfires when Meta declines —rejection_reasonis attached.template.paused/template.disabledfire when Meta pauses or disables a previously-approved template (e.g. sustained low quality). The template stops resolving as approved, so subsequent sends returntemplate_not_approveduntil it recovers.
Estimate cost before a broadcast
curl https://api.tyxter.com/v1/templates/tmpl_.../estimate-cost \
-H "authorization: Bearer $TYXTER_API_KEY" \
-H "content-type: application/json" \
-d '{ "recipients": 10000 }'Returns the meter id, per-recipient BRL amount, and total. Approved templates only — drafts and rejected templates 400 so you don't budget for sends Meta won't accept.
Inspect template analytics
Analytics combine TemplateVersion approval outcomes with message sends bound totemplate_id. Legacy rows that only have template_name are included as a compatibility fallback.
Use in a send
curl https://api.tyxter.com/v1/messages \
-H "authorization: Bearer $TYXTER_API_KEY" \
-H "content-type: application/json" \
-d '{
"channel": "whatsapp",
"sender": { "type": "whatsapp_phone_number", "id": "pn_..." },
"recipient": { "type": "phone_e164", "id": "+5511999999999" },
"message": {
"type": "template",
"template": {
"name": "order_update",
"language": "pt_BR",
"variables": { "1": "Diego", "2": "ORD-123" }
}
}
}'SDK lifecycle
const template = await tyxter.templates.create({
name: "order_update",
language: "pt_BR",
category: "utility",
components: [
{
type: "BODY",
text: "Olá {{1}}",
example: { body_text: [["Diego"]] },
},
],
});
await tyxter.templates.update(template.id, {
components: [
{
type: "BODY",
text: "Olá {{1}}, seu pedido foi atualizado.",
example: { body_text: [["Diego"]] },
},
],
});
await tyxter.templates.submit(template.id);
await tyxter.templates.analytics(template.id);