> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Troubleshooting: WhatsApp Business Calling pre-flight gates

> Resolve every gate that fires before a WhatsApp call dials — country blocklist, recipient permission, wallet pre-flight, per-tenant daily cap, and the shared-WABA phone resolver — plus platform-side codes for call actions, initiation, and media provisioning.

# Troubleshooting: WhatsApp Business Calling pre-flight gates

An outbound WhatsApp Business call refuses to dial runs through a fixed chain
of pre-flight gates, and the error code on the response names the exact gate
that fired. This page maps each symptom back to that gate and the fix you
own, then covers the platform-side codes for call control, initiation, and
media provisioning that fire after the gates pass.

The happy-path setup — enabling calling, permission sources, initiating a
call — lives on the guide page
[WhatsApp Business Calling](/guides/whatsapp/business-calling). This page is
what you reach for when a code starts with `WHATSAPP_CALL` or
`WHATSAPP_CALLING` shows up in a response, log, or the dashboard KPI panel.

## 1. Symptom map

| Symptom                                                                      | Gate that fired                                          | Fix                                                            |
| ---------------------------------------------------------------------------- | -------------------------------------------------------- | -------------------------------------------------------------- |
| `POST /whatsapp/calling/calls` returns 422 before Meta is called             | Country block, permission missing, or connection invalid | Match the code below                                           |
| 422 `CALLING_REQUIRES_ELIGIBLE_PHONE` — enable or dial refused               | Country blocklist (US/CA/EG/VN/NG)                       | Use an eligible phone on the WABA                              |
| 422 `WHATSAPP_CALLING_NO_PERMISSION` / `WHATSAPP_CALLING_PERMISSION_MISSING` | Recipient has not granted call permission                | Send a `call_permission_request` template first                |
| 402 `WHATSAPP_CALLING_INSUFFICIENT_BALANCE`                                  | Wallet cannot cover the worst-case spend                 | Top up under Settings → Billing                                |
| 503 `WHATSAPP_CALLING_INSUFFICIENT_BALANCE`                                  | Wallet read itself failed — gate fails closed            | Retry; the 503 is a fail-closed posture, retryable             |
| 429 `WHATSAPP_CALLING_DAILY_CAP_EXCEEDED`                                    | Per-tenant daily cost cap reached (UTC day)              | Raise the cap or wait for the next UTC day                     |
| 422 `WHATSAPP_NO_CALLING_PHONE`                                              | Shared-WABA resolver found no callable phone             | Connect your own WABA or retry later                           |
| 503 `WHATSAPP_CALLING_MEDIA_NOT_PROVISIONED`                                 | Platform media edge down                                 | Platform-side; retry, and if it persists check the status page |
| 503 `WHATSAPP_SHARED_CALLING_QUALITY_PAUSED`                                 | The platform shared phone is quality-paused at Meta      | Bypass with your own WABA                                      |
| `WHATSAPP_CALL_INITIATE_FAILED` after the gates pass                         | Meta rejected `POST /{phone-number-id}/calls`            | Check the request shape; escalate with `request_id`            |
| `WHATSAPP_CALL_ACTION_FAILED` on accept/decline/terminate                    | Meta rejected the call-control action                    | Retry the action against the same call id                      |
| `WHATSAPP_CALLING_PROVISION_FAILED` on enable/disable toggle                 | Meta round-trip for the flag failed                      | Retry; escalate with the phone-number id                       |

## 2. The pre-flight gate chain, in order

Every `POST /whatsapp/calling/calls` walks these gates in this order. The
early gate returns first — a 422 permission code means the country and wallet
gates passed, and a 429 means country, permission, and wallet all passed.

1. **Connection lookup + access-token decrypt.** The
   `from_phone_number_id` must resolve to a connection with a working
   access token. Unknown id → 404 `WHATSAPP_NOT_CONNECTED`. A row with no
   resolved WABA id, or a token that fails to decrypt (key rotation,
   manual edit, re-encryption drift) → 422 `WHATSAPP_CONNECTION_INVALID`.
   Fix: reconnect the number through Embedded Signup under
   **Channels → WhatsApp**.
2. **Country blocklist.** Meta does not yet permit WhatsApp Business
   Calling from every country. The gate compares the destination (and, on
   the enable toggle, the origin phone) against the current blocked list —
   `US`, `CA`, `EG`, `VN`, `NG` at the time of writing. A hit → 422
   `CALLING_REQUIRES_ELIGIBLE_PHONE` with a `details` payload that lists
   the blocked codes, the phone country that triggered, and any
   calling-eligible phones already on your WABA. Fix: dial from an
   eligible phone on the same WABA, or add one. Meta updates the list
   periodically; **Channels → WhatsApp → Calling** shows the live set in
   the dashboard.
3. **Calling-enabled flag on the WABA phone.** Refused with the same
   422 as the country gate when calling has not been enabled on the
   origin number. Fix: enable it with
   `POST /whatsapp/connections/{phoneNumberId}/calling/enable` (owner /
   admin only) before dialing.
4. **Recipient is a known contact.** The `to` number must match an
   existing contact — the platform never auto-creates one at call time.
   Unknown recipient → 422 `WHATSAPP_CALLING_NO_PERMISSION` with a
   message that names the missing contact. Fix: create the contact first.
5. **Recipient call-permission grant.** The contact must hold an active
   `granted` row for the (contact, WABA, phone-number-id) triple.
   Missing, expired, or revoked → 422 `WHATSAPP_CALLING_NO_PERMISSION`,
   message say which of the three fired. Fix: send a
   `call_permission_request` template first; the dashboard's contact
   panel offers that template as a one-click CTA when it sees a missing
   grant. Inbound calls are not affected — this gate is outbound only.
6. **Wallet cover.** The gate prices the worst-case spend
   (`max_call_minutes` × per-minute rate for the destination country;
   `max_call_minutes` defaults to 60 and is overridable per org at
   **Settings → WhatsApp → Calling → Max call minutes**). Balance below
   the estimate → 402 `WHATSAPP_CALLING_INSUFFICIENT_BALANCE`, message
   names the required amount and current balance. A balance-read failure
   → 503 with the same code and a message to retry — the posture is
   fail-closed by design, so a wallet outage never debits a call the
   platform cannot price. Fix: top up under **Settings → Billing**.
7. **Per-tenant daily cost cap.** When the org set one
   (`organizations.settings.whatsapp_calling_daily_cap_cents`), the gate
   compares today's UTC spend against it. Over → 429
   `WHATSAPP_CALLING_DAILY_CAP_EXCEEDED`. Fix: raise the cap under
   **Settings → WhatsApp → Calling → Daily spend cap** or wait for the
   next UTC day.
8. **Meta round-trip.** All gates pass; the call dials. Only the codes in
   section 4 below can appear past this point.

Inbound calls run a narrower chain — gates 1, 2, and (on the shared-phone
path) the shared-WABA gates of section 3. Recipient permission and wallet
gates are outbound-only.

## 3. The shared-WABA resolver

When you dial through the platform's shared calling phone rather than
your own WABA, two gates join the chain at the resolver stage, either of
which short-circuits the rest:

* `WHATSAPP_NO_CALLING_PHONE` (422) — the resolver found no callable
  phone at all: no eligible WABA of yours and the shared phone unset or
  deregistered at Meta. Fix: run Embedded Signup and enable calling on
  your own number, or retry once the platform shared phone is live again.
* `WHATSAPP_SHARED_CALLING_QUALITY_PAUSED` (503) — the shared phone
  reports `quality_rating=RED` from Meta and the platform pauses all
  shared-origin calls until it recovers. Fix: dial through your own WABA
  to bypass, or wait for the platform to clear the pause. Quality probes
  are best-effort — a probe failure fails open.

## 4. Past-gate codes (call control, initiation, media)

* `WHATSAPP_CALL_INITIATE_FAILED` — Meta rejected the
  `POST /{phone-number-id}/calls` initiation after the pre-flight chain
  passed. Check the request body — in particular `sdp_offer` if you set
  it — then escalate to support with the response `request_id` if the
  shape is clean.
* `WHATSAPP_CALL_ACTION_FAILED` — Meta rejected a call-control action
  (`pre_accept`, `accept`, `reject`, `terminate`) on an inbound or
  outbound call. Retry the action against the same `meta_call_id`; a
  terminated-call id is terminal, so a 5xx retry loop returns the same
  code until the call ends.
* `WHATSAPP_CALL_PERMISSION_FAILED` — Meta refused a permission-request
  send, usually because the service window for the contact is closed.
  Re-engage the contact with a template first, then resend the
  `call_permission_request`.
* `WHATSAPP_CALLING_PROVISION_FAILED` — toggling the calling-enabled flag
  (enable/disable) or re-registering the webhook failed at the Meta
  round-trip. Retry once; escalate with the phone-number id when it
  persists.
* `WHATSAPP_CALLING_MEDIA_NOT_PROVISIONED` (503) — the platform's media
  edge for the WA-calling relay is down. All signalling passed; only the
  audio leg refused. This is platform-side — retry and, if it persists,
  check the status page; nothing on your account can fix it.

## 5. Retry class map

| Code                                                                     | HTTP | Retry class                                  |
| ------------------------------------------------------------------------ | ---- | -------------------------------------------- |
| `WHATSAPP_NOT_CONNECTED`                                                 | 404  | Deterministic — fix the connection           |
| `WHATSAPP_CONNECTION_INVALID`                                            | 422  | Deterministic — reconnect Embedded Signup    |
| `CALLING_REQUIRES_ELIGIBLE_PHONE`                                        | 422  | Deterministic — country gate                 |
| `WHATSAPP_CALLING_NO_PERMISSION` / `WHATSAPP_CALLING_PERMISSION_MISSING` | 422  | Deterministic — collect the grant first      |
| `WHATSAPP_CALLING_INSUFFICIENT_BALANCE` (402)                            | 402  | Deterministic — top up                       |
| `WHATSAPP_CALLING_INSUFFICIENT_BALANCE` (503)                            | 503  | Transient — fail-closed, safe to retry       |
| `WHATSAPP_CALLING_DAILY_CAP_EXCEEDED`                                    | 429  | Deterministic until the next UTC day         |
| `WHATSAPP_NO_CALLING_PHONE`                                              | 422  | Deterministic — bring your own WABA          |
| `WHATSAPP_SHARED_CALLING_QUALITY_PAUSED`                                 | 503  | Transient — bypass with your own WABA        |
| `WHATSAPP_CALL_ACTION_FAILED`                                            | 5xx  | Transient — retry with backoff, same call id |
| `WHATSAPP_CALLING_PROVISION_FAILED`                                      | 5xx  | Transient — retry once                       |
| `WHATSAPP_CALLING_MEDIA_NOT_PROVISIONED`                                 | 503  | Platform-side — check the status page        |

## 6. Where each code surfaces

* **API response.** The `error.code` field on the JSON body, with HTTP
  status in the table above. Error surfaces that carry a `details`
  payload (the country gate, the wallet gate) stuff the blocked list, the
  triggering country, or the balance numbers there so the dashboard can
  render the path forward without another round-trip.
* **Dashboard KPI panel.** The Calls tab at `/messages/whatsapp` shows a
  row per attempted outbound call with the gate code; the permission gate
  also drives the Call button's disabled state and its tooltip (missing,
  expired, or revoked) in the conversation header.

## 7. Escalation payload

When none of the fixes above resolves it, include all of the following in
the support ticket — each gate's failure is deterministic given four
inputs, so a complete record resolves in one pass:

* The full response body (the `error.code`, `error.message`, and any
  `error.details`) and the response's `request_id`.
* The request you sent: `to`, `from_phone_number_id`, and whether the
  shared platform phone or your own WABA was targeted.
* The contact id for the recipient and its current permission state (from
  `GET /whatsapp/calling/permissions?contact_id=...`).
* The UTC day and time the refusal fired, for daily-cap reports.

## See also

* [WhatsApp Business Calling guide](/guides/whatsapp/business-calling)
* [WhatsApp connection and re-authentication](/troubleshooting/whatsapp-connection)
* [Insufficient balance](/troubleshooting/insufficient-balance)
* [Spend caps](/troubleshooting/spend-caps-hit)
* [Error codes reference](/reference/error-codes)
