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:
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¶
- Classifies the failure.
whatsapp-cloud-sendandwhatsapp-cloud-webhookboth run Meta's code through the shared classifier, storingmetadata.failure_reason(machine-readable) andmetadata.user_message(what the inbox shows) alongside the raw provider text. - Records a send block. A
number-scope failure stampschannel_accounts.settings.health.send_blockand raises one unacknowledgedchannel_alertsrow (alert_type = number_send_blocked) — five failed sends produce one banner, not five. - Warns before the next send.
WhatsAppNumberStatusrenders 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). - Reads the truth from Meta.
whatsapp-number-health(edge function, JWT-authed, org-scoped) fetchesverified_name, name_status, new_name_status, status, quality_rating, code_verification_statusfor the number, stores it undersettings.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. - Clears itself, by whichever comes first: Meta's
phone_number_name_updatewebhook (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 functionwhatsapp-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¶
- WhatsApp Manager → Account tools → Phone numbers.
- Check the display name status for the number.
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.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.- 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.