APIs & Integrações26 de fev. de 2026· 9 min leitura

How to Create and Manage Templates in WhatsApp Business API: Complete Guide

End-to-end guide covering the full template lifecycle in WhatsApp Business API: creation via API, JSON structure, variables, categories, Meta approval process, and best practices for first-time approval.

Templates are the backbone of any serious WhatsApp Business API integration. They're required to initiate conversations with users (when the 24-hour window is closed), and any proactive message — billing, order notification, appointment reminder — must go through them.

This guide covers the complete lifecycle: creating, structuring, submitting for approval, and managing templates, with production-ready code examples.

What Are Templates and When to Use Them

Templates (officially Message Templates) are Meta-pre-approved messages you can send outside the 24-hour customer service window. When an active conversation exists (user replied in the last 24h), you can send free-form text. Outside that window, only approved templates work.

Common use cases:

  • Order notifications — confirmation, shipment, delivery
  • Appointment reminders — consultation confirmation, follow-up
  • Billing and invoices — payment due, invoice available
  • OTP authentication — two-factor verification codes
  • Marketing and promotions — offers, coupons, launches (Marketing category)

Template Categories

Meta classifies all templates into three categories. The category affects the cost per conversation and content rules:

Warning: Meta can automatically reclassify your template if the content doesn't match the declared category. Declare correctly from the start.

Template JSON Structure

Every template is composed of components. Available components are:

{
  "name": "order_update",
  "language": "en_US",
  "category": "UTILITY",
  "components": [
    {
      "type": "HEADER",
      "format": "TEXT",
      "text": "Order #{{1}} Updated"
    },
    {
      "type": "BODY",
      "text": "Hi {{1}}! Your order #{{2}} has been {{3}} and will arrive by {{4}}. Track it in real time via the link below."
    },
    {
      "type": "FOOTER",
      "text": "Reply STOP to unsubscribe from notifications."
    },
    {
      "type": "BUTTONS",
      "buttons": [
        {
          "type": "URL",
          "text": "Track Order",
          "url": "https://yoursite.com/track/{{1}}"
        }
      ]
    }
  ]
}

HEADER Component

The header is optional but significantly increases open rates. It can be:

  • TEXT — Plain text (supports one variable {{1}})
  • IMAGE — Public image URL (JPEG/PNG, up to 5MB)
  • VIDEO — Public video URL (MP4, up to 16MB)
  • DOCUMENT — Public PDF URL

For media headers, you don't provide the URL in the template itself — it's passed dynamically when sending the message.

BODY Component

The body is the only required component. 1024-character limit. Supports:

  • Dynamic variables: {{1}}, {{2}}, {{3}}... (indexed starting at 1)
  • Basic formatting: *bold*, _italic_, ~strikethrough~, ``code``

FOOTER Component

Static text (no variables), maximum 60 characters. Ideal for opt-out notices.

BUTTONS Component

Up to 10 buttons per template. Available types:

Variable Rules

Variables follow the {{N}} pattern where N is a sequential integer starting at 1.

Critical rules:

  1. No index gaps — If you use {{1}} and {{3}}, {{2}} must also exist
  2. Examples required — Every variable needs an example in the example field
  3. No variables in footers — Footers are always static
  4. URL variables — Only the suffix can be a variable: https://yoursite.com/{{1}} ✅ | https://{{1}}.com

Correct example with example field:

{
  "type": "BODY",
  "text": "Hi {{1}}! Your order #{{2}} has been confirmed.",
  "example": {
    "body_text": [["John Smith", "US-456789"]]
  }
}

Creating a Template via API

Endpoint

POST https://graph.facebook.com/v21.0/{WABA_ID}/message_templates

Authentication

Use a System User Token with whatsapp_business_management permission. The temporary test token from the dashboard is not suitable for production.

curl -X POST \
  "https://graph.facebook.com/v21.0/YOUR_WABA_ID/message_templates" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "appointment_confirmation",
    "language": "en_US",
    "category": "UTILITY",
    "components": [
      {
        "type": "BODY",
        "text": "Hi {{1}}! Your appointment for {{2}} at {{3}} has been confirmed. To cancel, reply CANCEL.",
        "example": {
          "body_text": [["Maria Smith", "medical consultation", "2:30 PM"]]
        }
      },
      {
        "type": "FOOTER",
        "text": "Reply STOP to unsubscribe."
      }
    ]
  }'

Success Response

{
  "id": "123456789012345",
  "status": "PENDING",
  "category": "UTILITY"
}

The initial status is always PENDING. After Meta's review, it changes to APPROVED or REJECTED.

Practical Examples

Template with Image Header

{
  "name": "weekly_promotion",
  "language": "en_US",
  "category": "MARKETING",
  "components": [
    {
      "type": "HEADER",
      "format": "IMAGE",
      "example": {
        "header_handle": ["https://yoursite.com/promo-banner.jpg"]
      }
    },
    {
      "type": "BODY",
      "text": "🔥 *Exclusive offer for {{1}}!*\n\nToday only: {{2}}% off storewide. Use code *{{3}}* at checkout.",
      "example": {
        "body_text": [["you", "30", "FLASH30"]]
      }
    },
    {
      "type": "FOOTER",
      "text": "Valid today only."
    },
    {
      "type": "BUTTONS",
      "buttons": [
        {
          "type": "URL",
          "text": "Shop the Sale",
          "url": "https://store.yoursite.com/promo/{{1}}",
          "example": ["flash-sale"]
        },
        {
          "type": "QUICK_REPLY",
          "text": "Not interested"
        }
      ]
    }
  ]
}

Template with Quick Reply and CTA Buttons

{
  "name": "satisfaction_survey",
  "language": "en_US",
  "category": "UTILITY",
  "components": [
    {
      "type": "BODY",
      "text": "Hi {{1}}! How was your experience with our support on {{2}}?\n\nYour feedback helps us improve. 🙏",
      "example": {
        "body_text": [["Carlos", "today"]]
      }
    },
    {
      "type": "BUTTONS",
      "buttons": [
        {
          "type": "QUICK_REPLY",
          "text": "😊 Great"
        },
        {
          "type": "QUICK_REPLY",
          "text": "😐 Average"
        },
        {
          "type": "QUICK_REPLY",
          "text": "😞 Poor"
        }
      ]
    }
  ]
}

OTP Authentication Template

{
  "name": "verification_code",
  "language": "en_US",
  "category": "AUTHENTICATION",
  "components": [
    {
      "type": "BODY",
      "text": "Your verification code is *{{1}}*. Valid for 10 minutes. Do not share it with anyone.",
      "example": {
        "body_text": [["483920"]]
      }
    },
    {
      "type": "BUTTONS",
      "buttons": [
        {
          "type": "COPY_CODE",
          "example": "483920"
        }
      ]
    }
  ]
}

For authentication templates with COPY_CODE buttons, Meta requires the body to follow a specific format. The structure above guarantees approval.

Meta's Approval Process

After submitting, the template goes through automated (and eventually human) review:

  1. PENDING — Awaiting review (0–24h normally, can take up to 72h)
  2. APPROVED — Approved and ready to use
  3. REJECTED — Rejected with reason (rejection_reason)
  4. PAUSED — Paused due to low quality (high user block rate)
  5. DISABLED — Permanently disabled for serious violations

Average approval times:

  • Simple AUTHENTICATION and UTILITY templates: usually < 1 hour
  • MARKETING templates: 2–24 hours
  • Templates with media or URL buttons: may take longer

Querying Status via API

curl "https://graph.facebook.com/v21.0/YOUR_WABA_ID/message_templates?name=verification_code" \
  -H "Authorization: Bearer YOUR_TOKEN"
{
  "data": [
    {
      "name": "verification_code",
      "status": "APPROVED",
      "id": "123456789012345",
      "language": "en_US",
      "category": "AUTHENTICATION"
    }
  ]
}

Editing Existing Templates

You can edit the content of an approved template, but you cannot change the name, language, or category. Editing puts the template back to PENDING.

curl -X POST \
  "https://graph.facebook.com/v21.0/TEMPLATE_ID" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "components": [
      {
        "type": "BODY",
        "text": "Hi {{1}}! Your appointment for {{2}} at {{3}} is confirmed. Please arrive 10 minutes early. To cancel, reply CANCEL.",
        "example": {
          "body_text": [["Ana Smith", "physiotherapy", "9:00 AM"]]
        }
      }
    ]
  }'

Deleting Templates

Templates can only be deleted if they are not being used in active conversations. After deletion, the name is blocked for 30 days.

curl -X DELETE \
  "https://graph.facebook.com/v21.0/YOUR_WABA_ID/message_templates?name=verification_code&hsm_id=TEMPLATE_ID" \
  -H "Authorization: Bearer YOUR_TOKEN"

Best Practices for Fast Approval

What Meta Rejects

These patterns cause immediate rejection:

  • Variables without examples — Never submit a template without the example field filled in
  • Ambiguous content"Your update has arrived" without context → rejected. Be specific: "Your order #{{1}} has shipped"
  • Suspicious or shortened URLsbit.ly/xyz → rejected. Always use your real domain
  • Mixed languages — Template declared as en_US with chunks in another language → inconsistency
  • Promotions disguised as utilityUTILITY template with clearly promotional language → reclassified and potentially rejected
  • Explicit sensitive data — Don't ask for SSN, passwords, or card numbers in the body
  • Excessive urgency"LAST DAY!!!", "HURRY!!!" — raises spam flags

What Guarantees Approval

  1. Realistic examples — Use example data that actually makes sense for the template
  2. Clear opt-out — For marketing, include cancellation instructions in the footer
  3. Specific CTA"View my order" is better than "Click here"
  4. Correct category — Don't try to save costs by putting marketing as utility
  5. Natural text — Write like a human. Avoid text that sounds bot-generated or spammy

Monitoring Quality Post-Approval

After approval, Meta monitors whether users are blocking messages sent via your template. If the block rate exceeds the threshold, the template moves to PAUSED and may become DISABLED.

To maintain quality:

  • Send only to users who opted in to receive messages
  • Don't overwhelm with daily marketing messages
  • Personalize variables — generic messages have higher block rates

Complete Node.js Flow

const axios = require('axios')

const WABA_ID = process.env.WHATSAPP_WABA_ID
const TOKEN = process.env.WHATSAPP_TOKEN

async function createTemplate(templateData) {
  try {
    const response = await axios.post(
      `https://graph.facebook.com/v21.0/${WABA_ID}/message_templates`,
      templateData,
      {
        headers: {
          Authorization: `Bearer ${TOKEN}`,
          'Content-Type': 'application/json',
        },
      }
    )
    console.log('Template created:', response.data)
    return response.data
  } catch (error) {
    const errorData = error.response?.data?.error
    console.error('Error creating template:', errorData)
    throw error
  }
}

async function getTemplateStatus(templateName) {
  const response = await axios.get(
    `https://graph.facebook.com/v21.0/${WABA_ID}/message_templates`,
    {
      params: { name: templateName },
      headers: { Authorization: `Bearer ${TOKEN}` },
    }
  )
  return response.data.data[0]
}

// Usage example
const template = {
  name: 'delivery_notification',
  language: 'en_US',
  category: 'UTILITY',
  components: [
    {
      type: 'BODY',
      text: 'Your order #{{1}} was delivered on {{2}}. Rate your experience at: {{3}}',
      example: {
        body_text: [['US-12345', 'Feb 15, 2026', 'https://rate.yoursite.com']],
      },
    },
  ],
}

createTemplate(template).then(async (created) => {
  // Wait for approval (simple polling)
  let status = 'PENDING'
  while (status === 'PENDING') {
    await new Promise((r) => setTimeout(r, 30000)) // 30s
    const info = await getTemplateStatus(template.name)
    status = info.status
    console.log(`Status: ${status}`)
  }
  console.log(`Template ${status === 'APPROVED' ? '✅ approved!' : '❌ rejected.'}`)
})

Summary

Templates are the entry point for proactive communication on WhatsApp. Mastering their creation and management is fundamental for any production integration.

The most critical points:

  • Always fill in the example field — it's the most common rejection reason
  • Use the correct category — Meta reclassifies and may penalize
  • Monitor quality — paused templates affect the entire account
  • Names and languages are immutable — plan carefully before creating

With these fundamentals, you have everything you need to create templates that pass on the first try and work reliably in production.

Artigos Relacionados

  • APIs & Integrações

    Como Criar e Gerenciar Templates na WhatsApp Business API: Guia Completo

  • APIs & Integrações

    Como Configurar Webhooks na WhatsApp Business API: Guia Técnico Completo

  • APIs & Integrações

    WhatsApp Business API: Guia Completo Para Empresas em 2026

.