Skip to content

WhatsApp Cloud send blocks

The symptom

A WhatsApp Cloud number is connected and receiving normally. Agents can chat with it from their own phones. But every outbound message — free-form text and approved templates — fails with:

(#131037) WhatsApp provided number needs display name approval before message can be sent.

This reads like a ConnectGain bug. It is not: Meta gates sending on the phone number's display name review, and inbound traffic is unaffected. A number can therefore go from "sends fine" to "refuses everything" with no visible change on our side — typically because someone just submitted or changed the display name in WhatsApp Manager, putting name_status into PENDING_REVIEW.

Real timeline from the incident that prompted this page (WhatsApp-provided test number +1 555-962-8162):

Time (UTC) Event
15:13 Template hello sent → DELIVERED → READ
~15:15 Display name submitted for review in WhatsApp Manager
15:16 → 15:25 Four sends (text and template) fail with 131037
throughout 10 inbound messages arrive normally

GET /{phone-number-id}?fields=name_status returned PENDING_REVIEW — the only place that state was visible.

Number-level vs message-level failures

Meta's error codes mix two very different things, and ConnectGain now separates them:

Scope Meaning Examples
number Nothing can be sent from this number until an admin fixes something 131037 display name, 131042 billing, 131031/368 restricted, 133xxx registration, 190 token
conversation This thread is blocked, others are fine 131047 (24-hour window), 131056
message This message was rejected; the next may go through 131026, 131049, 131051, 132xxx templates

Only number-scope failures raise a channel alert. Retrying them is pointless, which is exactly what agents did before ConnectGain said so.

What ConnectGain does

  1. Classifies the failure. whatsapp-cloud-send and whatsapp-cloud-webhook both run Meta's code through the shared classifier, storing metadata.failure_reason (machine-readable) and metadata.user_message (what the inbox shows) alongside the raw provider text.
  2. Records a send block. A number-scope failure stamps channel_accounts.settings.health.send_block and raises one unacknowledged channel_alerts row (alert_type = number_send_blocked) — five failed sends produce one banner, not five.
  3. Warns before the next send. WhatsAppNumberStatus renders that state on the channel card (Settings → Channels — always, including the display name and its review status) and at the top of the inbox thread (only when the number cannot send).
  4. Reads the truth from Meta. whatsapp-number-health (edge function, JWT-authed, org-scoped) fetches verified_name, name_status, new_name_status, status, quality_rating, code_verification_status for the number, stores it under settings.health.phone_number, and raises or clears the block accordingly. The channel card probes it on mount when the stored snapshot is stale (> 10 minutes) or a block stands; "Check again" forces a probe.
  5. Clears itself, by whichever comes first: Meta's phone_number_name_update webhook (approval or decline), a health check, or a successful send — which also acknowledges the open alert.

name_status values

Value Can send?
APPROVED, AVAILABLE_WITHOUT_REVIEW Yes
PENDING_REVIEW No — review in progress, usually hours (up to 48)
DECLINED No — submit a compliant name
EXPIRED No — re-submit the display name
NONE No — no display name set yet

An undocumented value is reported as "unknown" rather than a false all-clear.

Setting the display name from ConnectGain

Settings → Channels → the WhatsApp Cloud card shows the number's display name and its review state, with a Change action (owner/admin only). The dialog:

  • checks the name against Meta's published guidelines before submitting — Meta's documented rules (minimum 3 characters, no Meta product names, no "Official"/"Verified" claims, no phone numbers, no bare generic terms) disable the button; heuristics BSPs report as rejection causes (emoji, ALL CAPS, www., prices, slogans, a name unrelated to the organization) are shown as warnings only, because a real brand must still be able to submit;
  • states the requirements no validator can settle: branding consistency with your website, Meta business verification, and that the number cannot send while the review runs;
  • submits via POST /{phone-number-id}?new_display_name=… (edge function whatsapp-number-health, action: "set_display_name") and shows Meta's own rejection text when Meta refuses.

The rules live in the platform; Meta remains the only authority, so the validator never blocks on a rule that is not Meta's own.

After a display name is approved, Meta may require the number to be re-registered (POST /{phone-number-id}/register) before it sends again. If sending is still refused after approval, re-run activation for the number.

Enable the approval webhook

phone_number_name_update tells us the moment Meta approves or declines a name, which is what clears the block without anyone polling. It is handled in whatsapp-cloud-webhook, but the field must be ticked for the whatsapp_business_account object in the Meta App dashboard → Webhooks. Without it, the block still clears — just later, on the next health check or successful send.

Fixing it as an admin

  1. WhatsApp Manager → Account tools → Phone numbers.
  2. Check the display name status for the number.
  3. PENDING_REVIEW: wait. Nothing to reconnect in ConnectGain — sending resumes on its own, and the banner clears on the next successful send or health check.
  4. DECLINED: submit a name that matches the business (see Meta's display name guidelines). WhatsApp-provided test numbers (+1 555…) always need an approved display name before they can send anything.
  5. Not a display name problem? The banner names the actual block — billing (WhatsApp Manager → Settings → Billing & payments), an account restriction (Account tools → Insights), or an expired token (reconnect the channel).

Checking a number by hand

curl -s "https://graph.facebook.com/v22.0/<PHONE_NUMBER_ID>\
?fields=verified_name,name_status,new_name_status,display_phone_number,status,quality_rating" \
 -H "Authorization: Bearer <ACCESS_TOKEN>"
{
 "verified_name": "Appgain",
 "name_status": "PENDING_REVIEW",
 "display_phone_number": "+1 555-962-8162",
 "status": "CONNECTED",
 "quality_rating": "GREEN"
}

status: CONNECTED and a GREEN quality rating say nothing about whether the number may send — only name_status does.