Skip to main content

Troubleshoot WHATSAPP_TEMPLATE_VAR_COUNT_MISMATCH

You send a WhatsApp template and get an HTTP 400 back before any message goes out. Orbit validates the parameter list against the template body at accept time and refuses the send when the two disagree, so a bad payload is a cheap 400 instead of a rejected Meta delivery.
This page covers the send-side parameter check — the count and shape of the values you pass. For Meta-side template problems (pending, rejected, paused, reclassified, 24-hour window), see WhatsApp template status.
An example response:
The same code fires for an empty value, with reason: empty_param_value and a details.empty_keys list naming the slots:

How parameter indexing works

A WhatsApp template body declares positional placeholders {{1}}, {{2}}, … {{N}}. On the send you pass template_params, an object whose keys are the 1-based placeholder numbers and whose values are the replacement strings:
The pre-flight check reads the approved template body, counts its placeholders, and compares that count against the parameters you supplied. Two call-site shapes count as the parameter source: the template_params object, or (for outbound templates saved with a Media header) the Meta-native metadata.templateComponents array — whichever is non-empty. Results of the comparison:
  • Fewer parameters than placeholders → 400, reason: missing_params.
  • More parameters than placeholders → 400, reason: extra_params.
  • Parameters on a zero-placeholder template → 400, reason: extra_params_on_zero_placeholder_template.
  • Same count but a value is an empty or whitespace-only string → 400, reason: empty_param_value, with details.empty_keys.
A count match only clears this check. If the keys are non-positional ("name", "first_name") or skip a number ("1", "3" for a two-placeholder template), the provider rejects the send again at build time with WHATSAPP_TEMPLATE_PARAM_MISSING even when the count lines up. Keys must be exactly the contiguous series "1""N".

Common mismatches

Work the table in order — each row is one fixable cause.

Read the template body you are sending against

Do not count placeholders from memory — templates drift. Fetch the current body and count the {{N}} markers yourself:
Fetch the full record at GET /api/v1/messages/templates/{id} for the complete body text. The count check only runs when the template body (or header) text is stored locally — authentication (OTP) templates and Meta-synced templates whose body lives inside the stored components defer to Meta instead, so a mismatch there surfaces as a harder-to-read 132000/131008 rejection after the fact.

Fix by example

Template with a Media header variable and one body placeholder:
  • Header: image with a variable URL (or fixed media)
  • Body: Your order {{1}} shipped today.
The correct send payload passes the header component through metadata.templateComponents and the body value through template_params:
The body placeholder {{1}} gets one parameter, the header variable rides in templateComponents, and the pre-flight passes. For a text-only template, drop metadata and fill every {{N}} from template_params alone.

Validate before you send

The remediation loop is short — treat the 400 as a compile error:
  1. Fetch the template body (above) and count {{N}} placeholders.
  2. Build template_params with keys "1""N" in that order.
  3. Confirm no value is empty or whitespace-only.
  4. For header/button variables, move them into metadata.templateComponents in the Meta component shape.
  5. Re-send. The same check runs on every WhatsApp template request, so a fixed payload clears it immediately.
In the dashboard template editor, the preview fills sample values into each placeholder — use it to confirm which slots the template expects before wiring your send code.

Escalation checklist

If you have worked the page and the same 400 keeps coming back, email whatsapp-support@devotel.io with:
  1. The template name and language exactly as Meta has them (order_shipped + en_US).
  2. The full error response body, including details.expected, details.supplied, and details.reason.
  3. The template body text as fetched from GET /api/v1/messages/templates/{id}.
  4. The exact request payload you sent, with phone numbers and parameter values redacted.

See also

  • WhatsApp template status — Meta-side approval, rejection, and quality gates. This page covers the send-side parameter pre-flight; that page covers everything Meta decides after the template exists.
  • Templates API — the create/read/update contract for the endpoints used above.
  • WhatsApp 24h freeform window — the window rules that decide whether a template send is required at all.