Authentication Bulk Management

Use upsert_message_templates to create or update authentication templates across many languages in one call, and use message_template_previews to preview localized text before committing.

What Bulk Management Does

Two endpoints help when you need authentication templates in many languages or want to preview the localized text before creating templates:

EndpointPurpose
POST /<WABA_ID>/upsert_message_templatesCreate or update authentication templates across multiple languages in a single call
GET /<WABA_ID>/message_template_previewsGenerate preview body, footer, and button text in any combination of languages and configuration options — without creating a template

Both are specific to authentication templates (the preset-text structure makes localized previews tractable). Other categories don't have an upsert endpoint or a preview API.

When to Use Each

  • Use previews before deciding which language/security/expiration combinations to ship — see exactly what the user will see in each language.
  • Use upsert when you've decided on a configuration and want to roll it out across N languages in one transactional API call rather than N create/update calls. Especially useful when adding or refreshing language coverage.

Upsert Message Templates

POST /<WABA_ID>/upsert_message_templates creates a template if one with that name+language does not exist, or updates the existing one if it does. The same call can target multiple languages — Meta returns a per-language status array.

If you mix create and update in one call (some languages already exist, others don't), Meta handles each independently and returns the per-language outcome.

Upsert Payload Shape

{
  "name": "<NAME>",
  "languages": ["en_US", "es_ES", "fr"],
  "category": "AUTHENTICATION",
  "components": [
    {
      "type": "BODY",
      "add_security_recommendation": true
    },
    {
      "type": "FOOTER",
      "code_expiration_minutes": 10
    },
    {
      "type": "BUTTONS",
      "buttons": [
        {
          "type": "OTP",
          "otp_type": "<OTP_TYPE>",
          "supported_apps": [
            {
              "package_name": "<PACKAGE_NAME>",
              "signature_hash": "<SIGNATURE_HASH>"
            }
          ]
        }
      ]
    }
  ]
}

languages is the multi-language replacement for the singular language field used in single-template create.

Property Differences vs Single Create

All standard authentication template create properties are supported, with three exceptions:

PropertySingle createUpsert
languageRequired stringNot supported — use languages array instead
text (button label)OptionalNot supported — labels fall back to localized defaults
autofill_textOptionalNot supported — falls back to localized default

If you need custom button labels, use single-create per language instead.

Example: Bulk Copy-Code in 3 Languages

curl 'https://graph.facebook.com/<GRAPH_VERSION>/<WABA_ID>/upsert_message_templates' \
  -H 'Authorization: Bearer <ACCESS_TOKEN>' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "authentication_code_copy_code_button",
    "languages": ["en_US", "es_ES", "fr"],
    "category": "AUTHENTICATION",
    "components": [
      { "type": "BODY", "add_security_recommendation": true },
      { "type": "FOOTER", "code_expiration_minutes": 10 },
      {
        "type": "BUTTONS",
        "buttons": [
          { "type": "OTP", "otp_type": "COPY_CODE" }
        ]
      }
    ]
  }'

Response — one entry per language, each with its own template id and review status:

{
  "data": [
    { "id": "954638012257287", "status": "APPROVED", "language": "en_US" },
    { "id": "969725527415202", "status": "APPROVED", "language": "es_ES" },
    { "id": "969725530748535", "status": "APPROVED", "language": "fr" }
  ]
}

Authentication templates often auto-approve (no manual review needed) when the structure matches the preset format and the security/expiration values are within range — that's why this example shows APPROVED immediately. For other categories or non-trivial templates, expect PENDING.

Example: Bulk One-Tap with Update

This call updates the existing en_US template (if any) and creates new es_ES and fr templates with one-tap autofill buttons:

curl 'https://graph.facebook.com/<GRAPH_VERSION>/<WABA_ID>/upsert_message_templates' \
  -H 'Authorization: Bearer <ACCESS_TOKEN>' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "authentication_code_autofill_button",
    "languages": ["en_US", "es_ES", "fr"],
    "category": "AUTHENTICATION",
    "components": [
      { "type": "BODY", "add_security_recommendation": true },
      { "type": "FOOTER", "code_expiration_minutes": 15 },
      {
        "type": "BUTTONS",
        "buttons": [
          {
            "type": "OTP",
            "otp_type": "ONE_TAP",
            "supported_apps": [
              {
                "package_name": "com.example.luckyshrub",
                "signature_hash": "K8a/AINcGX7"
              }
            ]
          }
        ]
      }
    ]
  }'

For one-tap and zero-tap, see OTP Handshake & Android SDK for the runtime side.

Message Template Previews

GET /<WABA_ID>/message_template_previews returns the actual body, footer, and button text in your requested languages — useful for previewing what your users will see before committing to a template configuration.

Query parameters:

ParameterRequired?Notes
categoryYesMust be AUTHENTICATION
languagesOptionalComma-separated list (e.g. en_US,es_ES). Omit to get all supported languages.
add_security_recommendationOptionaltrue to include "For your security…" string. Omit or false to exclude.
code_expiration_minutesOptionalInteger 1–90. If included, footer string with this number is returned. Omit to exclude footer.
button_typesRequiredComma-separated. For authentication this must be OTP.

Example: Preview en_US + es_ES with Security and Expiration

curl 'https://graph.facebook.com/<GRAPH_VERSION>/<WABA_ID>/message_template_previews?category=AUTHENTICATION&languages=en_US,es_ES&add_security_recommendation=true&code_expiration_minutes=10&button_types=OTP' \
  -H 'Authorization: Bearer <ACCESS_TOKEN>'

Response:

{
  "data": [
    {
      "body": "*{{1}}* is your verification code. For your security, do not share this code.",
      "buttons": [
        { "autofill_text": "Autofill", "text": "Copy code" }
      ],
      "footer": "This code expires in 10 minutes.",
      "language": "en_US"
    },
    {
      "body": "Tu código de verificación es *{{1}}*. Por tu seguridad, no lo compartas.",
      "buttons": [
        { "autofill_text": "Autocompletar", "text": "Copiar código" }
      ],
      "footer": "Este código caduca en 10 minutos.",
      "language": "es_ES"
    }
  ]
}

The preview is exactly what would render if you created the template with the same configuration. Use it for design reviews, customer support copy, or to validate that a language code is supported before adding it to your upsert_message_templates call.

Related

  • Authentication MessagesOTP and verification code delivery via WhatsApp: copy-code, one-tap, and zero-tap templates, linked device security, the 'I didn't request a code' webhook, and PendingIntent deprecation.
  • TemplatesTemplate lifecycle, categories, and management through Dualhook and Meta Graph API.
Browse more docsStart Free Trial