← Knowledge Hub
SME Automation & AI WhatsApp

Debugging WhatsApp Business API Integrations: Five Real Failure Modes

Most tutorials show you how to send your first WhatsApp message. Very few tell you why it stops working three days after you deploy. This piece is for developers and technical founders who are past "hello world" and into the frustrating middle ground where the API returns success but nothing arrives.

Everything here came from a production integration. No hypotheticals.

The API returns success. The message never arrives. That's the dangerous kind of failure.

1. Template Name and Language Mismatches

Meta's message template system requires exact string matching on both the template name and the language code. A template named booking_confirmation is not the same as Booking_Confirmation. The API will return a success response either way — the rejection happens downstream, silently, before the message reaches the user.

Fix: Always copy the template name and language code directly from Meta Business Manager. Do not retype them. Store them as constants, not strings scattered across your codebase. Even one character difference in casing or punctuation is enough to silently drop the message.

2. Named vs. Numbered Template Parameters

The WhatsApp Business API accepts two formats for template variable substitution: numbered ({{1}}, {{2}}) and named ({{name}}, {{date}}). Mixing these within a single request breaks template rendering. The API does not always tell you which format a given template expects — you need to check the template definition in Business Manager.

Fix: Inspect the template in Business Manager before writing code. Match the parameter format exactly. If you're migrating an existing integration, audit every template — they may not all use the same format.

3. Webhook Verify-Token Failures

When you register a webhook with Meta, they send a GET request to your endpoint with a hub.verify_token parameter. Your server must echo back the hub.challenge value to complete verification. If the token comparison uses a non-constant-time string comparison — which most naive implementations do — timing attacks can cause intermittent verification failures in high-load environments.

Fix: Use a constant-time comparison function: hash_equals() in PHP, crypto.timingSafeEqual() in Node.js.

4. The Serverless Timing Bug

This is the most subtle failure on this list — and the hardest to catch because it only manifests in production. In a serverless or edge-function environment, the process terminates as soon as an HTTP response is sent. If you're triggering the WhatsApp send inside a fire-and-forget async function called without await, after the response has already been returned — the runtime kills the function before the HTTP request to Meta's API completes.

The symptom: confirmations work perfectly in local development and fail silently in production. No error. No log entry. Nothing.

Fix: Await the WhatsApp send before returning your HTTP response. If the send genuinely must be non-blocking, use a proper background job queue — not a detached promise that a serverless runtime can kill.

5. Phone Number Format Inconsistency

Meta requires phone numbers in E.164 format (e.g., +2348090844442). Numbers stored with leading zeros, spaces, or non-E.164 country-code prefixes will fail to route. The error is typically a generic "recipient not found" with no further detail.

Fix: Normalise all phone numbers to E.164 at the point of entry — not at the point of sending. Strip spaces and non-numeric characters, prepend the country code, and enforce this at data collection time.

The Common Thread

Every failure on this list shares the same characteristic: the API returns a success response, or no error at all, and the message is dropped downstream. Build logging at every step before you assume the integration is working. And test in a production-equivalent environment — local development won't surface the serverless timing bug.

If you can't see it failing, you can't know it's working.

Have a question about this? Talk to us — we're happy to go deeper.