> ## 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 re-authentication and Embedded Sign-up recovery

> Work the whole re-auth family — token exchange versus selection errors on the connect flow, a stored token that expired, business-verification gates, the WABA-linked-elsewhere conflict, and the platform-side Meta app flag — with per-family recovery and a back-to-green verify.

# Troubleshooting: WhatsApp re-authentication and Embedded Sign-up recovery

Eleven error codes cover the connect and re-authenticate lifecycle of a
WhatsApp Business Account (WABA): the Embedded Sign-up run itself, the
selection you make inside it, and the token the connection stores
afterward. When any of them fires, the fix is almost always one clean
re-run of the connect flow — this page decodes the family so you run it
once, not five times.

The recovery endpoint for the whole family is `POST /api/v1/whatsapp/connect`,
the same call Embedded Sign-up makes. Every family below resolves back to
"run connect again" — the differences are what you change in Meta Business
Manager first, and how you confirm the recovery took. The send-layer codes
(`WHATSAPP_NOT_CONNECTED`, `WHATSAPP_CONNECTION_INVALID`, `WHATSAPP_TOKEN_EXPIRED`)
are decoded on [WhatsApp connection recovery](/troubleshooting/whatsapp-connection);
this page owns everything the connect flow itself returns.

<Note>
  These are tenant-owned controls: the recovery steps act on your Meta
  Business Manager, your WABA, and your Orbit organization's channel
  settings. `WHATSAPP_META_APP_DISABLED` is the one exception — it is a
  platform-side flag and is marked accordingly below.
</Note>

***

## Owner split: re-auth vs selection vs ownership

The single most common mis-route in this family is treating a selection
error as a re-auth error — the dashboard says "connection failed," you
re-run the sign-up popup, and it fails identically because the mistake
was the (WABA, phone) pair, not the token. Split the code first:

| Owner class                                          | Codes                                                                                                                                                       | What it means                                                                                                                                                             | First move                                                                                                                         |
| ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| **Re-auth** — token / Embedded-Sign-up failures      | `WHATSAPP_TOKEN_EXCHANGE_FAILED`, `WHATSAPP_ACCESS_TOKEN_EXPIRED`, `WHATSAPP_CONNECTION_NOT_FOUND`, `WHATSAPP_META_APP_DISABLED`                            | The OAuth code exchange failed, the stored token is dead, the connection row is missing, or the platform's Meta app is unavailable                                        | Re-run the connect flow from **Settings → Channels → WhatsApp → Connect**; do not change the WABA or phone                         |
| **Selection** — wrong WABA or phone chosen           | `WHATSAPP_INVALID_SELECTION`, `WHATSAPP_NO_WABA`, `WHATSAPP_NO_PHONE_NUMBER`, `WHATSAPP_BUSINESS_STATUS_PENDING`, `WHATSAPP_BUSINESS_VERIFICATION_REQUIRED` | The pair you sent to `/connect` does not match anything the token grants, or Meta reports zero WABAs / zero phones on this login, or verification gates block the account | Fix the pick (or the account state) in Meta Business Manager, then re-run connect with the corrected `waba_id` + `phone_number_id` |
| **Ownership** — phone not owned by the selected WABA | `WHATSAPP_PHONE_NOT_OWNED`, `WHATSAPP_WABA_ALREADY_LINKED`                                                                                                  | The phone number is not part of the WABA you picked, or the WABA already belongs to another Orbit organization                                                            | Attach the number to the WABA at Meta, or release the WABA from its current organization                                           |

***

## Re-auth family

### `WHATSAPP_TOKEN_EXCHANGE_FAILED` — the sign-up handshake itself

Meta auth codes are single-use and expire roughly ten minutes after the
OAuth dialog issues them. The exchange fails when the popup lingers past
the window, when the same `code` value is POSTed twice (the second POST
always loses), or when a browser extension rewrites the redirect. The
server rejects the exchange with a 400 and this code; a malformed 200
without a token body surfaces the same code as a 502.

**Recovery — re-run the exchange with a fresh code:**

1. Open a fresh Embedded Sign-up run from **Settings → Channels → WhatsApp → Connect**
   and finish the Meta dialog without pausing between steps.
2. Send the new code immediately — the dashboard does this in one motion;
   over the API it is:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/whatsapp/connect \
  -H "Authorization: Bearer $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"code": "<fresh code from the dialog>"}'
```

3. **Back to green:** a `{"data": {"status": "connected"}}` response, then
   `GET /api/v1/whatsapp/status` returns `connected: true`.

Retrying the same `code` is the anti-pattern to avoid — it burned the
code's single use on the first attempt, and every subsequent POST returns
`WHATSAPP_TOKEN_EXCHANGE_FAILED` again.

### `WHATSAPP_ACCESS_TOKEN_EXPIRED` — the stored long-lived token is dead

Meta long-lived tokens cap at 60 days, and a token revoked in Meta
Business Manager dies the same way. Orbit stores the resolved expiry at
connect time, so a connection that reports `WHATSAPP_CONNECTION_INVALID`
at send time is the lap-over of this same class — the connect-side
decoder here is the one the recovery popup surfaces.

**Recovery — refresh the token in place:**

1. Re-run **Settings → Channels → WhatsApp → Connect** with the same
   Facebook account and Business Manager as the original connect. Meta
   mints a fresh token for the existing WABA; Orbit overwrites the stored
   credential and re-links templates and phone numbers — nothing is
   deleted.
2. **Back to green:** `GET /api/v1/whatsapp/status` returns the same
   `waba_id` + `phone_number_id` pair as before, now with `connected:
   true` and `status: "active"`. A template send to a known-good
   recipient completes the check.

Deleting the channel and starting over is the refusal path you do not
want — it orphan-links the templates and phone numbers the refresh would
have re-bound.

### `WHATSAPP_CONNECTION_NOT_FOUND` — the row does not exist

A connect, rename, or default-setting call referenced a `phone_number_id`
the organization does not hold. The typical trigger is an integration
that cached a `phone_number_id` before someone disconnected (or renamed)
the channel, or a send scoped to a specific connection that was removed.

**Check the actual rows before you re-run anything:**

```bash theme={null}
curl https://api.orbit.devotel.io/api/v1/whatsapp/connections \
  -H "Authorization: Bearer $ORBIT_API_KEY"
```

Each row carries `waba_id`, `phone_number_id`, `label`, `connected`, and
`status` — this is the field-level check the whole family leans on. If
the row you need is genuinely gone, re-run the connect flow to recreate
it; if it exists under a different `label`, either re-point the caller to
the current `phone_number_id` or rename it back with the scoped rename
call. **Back to green:** the caller's cached id matches a row in this
list and `GET /api/v1/whatsapp/status` reports the picked connection
`active`.

### `WHATSAPP_META_APP_DISABLED` — the one platform-side flag

New connections fail while Devotel's Meta app is unavailable (typically a
Meta-side review). **Existing connections keep working** — this flag
blocks only the connect flow. Recovery is to wait for restoration;
status updates post to [status.orbit.devotel.io](https://status.orbit.devotel.io).
Retrying connect against this flag returns the same code, so pause the
loop and re-try on the next status check.

***

## Selection family

### `WHATSAPP_NO_WABA` — zero WABAs on the login

The token exchange succeeded, `debug_token` resolved, and the granted
WABA id list came back empty. The Meta login has no WhatsApp Business
Account — typically the wrong Facebook account, a Business Manager that
never claimed a WABA, or granular scopes granted without
`whatsapp_business_management`.

**Fix the account, then re-run:** log in to Meta Business Manager with
the account you will present, confirm a WABA exists under
**WhatsApp Manager**, and re-run connect. **Back to green:** the connect
response is `connected` with a real `waba_id` — no picker needed when the
login carries exactly one (WABA, phone) pair.

### `WHATSAPP_NO_PHONE_NUMBER` — a WABA with nothing to send from

Every granted WABA was enumerated and none of them carried a registered
phone number. A WABA without a phone can be picked in Embedded Sign-up
but cannot connect.

Register and verify a number in **Meta Business Manager → WhatsApp
Manager → Phone numbers**, then re-run connect. **Back to green:**
`GET /api/v1/whatsapp/status` returns a non-null `phone_number_id` and
`phone_display`.

### `WHATSAPP_INVALID_SELECTION` — the pair you sent is not offered

Multi-WABA logins return a `needs_selection` response listing every
`(waba_id, phone_number_id)` option the token grants; the dashboard (or
your integration) then re-POSTs `/connect` with the chosen pair. The
server validates the pair against the discovered options — a mismatched,
typo'd, or replayed pair is refused with a 422 and this code. That guard
exists so a connect call cannot be steered onto a WABA the granted token
cannot see.

**Re-read the option list from the first POST** (or from
`GET /api/v1/whatsapp/connections` when a partial state was stashed), then
re-send with an option that appears verbatim:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/whatsapp/connect \
  -H "Authorization: Bearer $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "<fresh code>",
    "waba_id": "1029384756",
    "phone_number_id": "5566778899"
  }'
```

**Back to green:** `connected` with the `waba_id` and `phone_number_id`
you named, visible in `GET /api/v1/whatsapp/status`.

### `WHATSAPP_BUSINESS_STATUS_PENDING` — verification still under review

The connection can complete while Meta's business review is open, but
higher messaging tiers and marketing-template approval stay locked until
the review resolves. This is a warning, not a hard failure.

Watch **Meta Business Settings → Business Info**; a pending decision
older than roughly two weeks is worth a resubmission with a cleaner
document set rather than a dispute. **Back to green:** the review
resolves at Meta and the tier reopens without a reconnect.

### `WHATSAPP_BUSINESS_VERIFICATION_REQUIRED` — verification never started

Meta blocks connects outright when the business has not completed
verification. The fix lives entirely in Meta Business Manager — submit
the verification documents under **Business Info → Business
verification**, then re-run connect. **Back to green:** a Meta-approved
verification followed by a successful `connected` response.

***

## Ownership family

### `WHATSAPP_PHONE_NOT_OWNED` — the number is not on the WABA you picked

You selected a phone number that is not registered under the WABA you
chose — a stale picker option, or a number that lives on a different WABA
of the same login. Re-running with the same pair returns the same code.

Attach the number to the intended WABA in **WhatsApp Manager → Phone
numbers**, or re-run the picker and choose the pair as Meta reports
them. **Back to green:** the `needs_selection` option list (or
`GET /api/v1/whatsapp/connections`) contains the pair and the re-POST
connects.

### `WHATSAPP_WABA_ALREADY_LINKED` — claimed by another organization

A WABA binds to exactly one Orbit organization. Connecting it to a second
organization is refused even when the Meta token and selection are clean.

Resolve the ownership dispute first: disconnect the WABA on the
organization that holds it (**Settings → Channels → WhatsApp →
Disconnect**, or the scoped `DELETE /api/v1/whatsapp/connections/:phoneNumberId`),
then connect it here. If neither organization is one you control, treat
it as an account dispute and contact
[whatsapp-support@devotel.io](mailto:whatsapp-support@devotel.io) with
the WABA id rather than attempting a takeover. **Back to green:** the
`GET /api/v1/whatsapp/connections` list on this organization picks up the
released WABA.

***

## What not to do

* **Do not re-POST the same `code` after a token-exchange failure.** The
  code is single-use; the second POST burns nothing and returns the same
  refusal. Always mint a fresh code from a fresh dialog run.
* **Do not delete the channel to "reset" a bad token.** A reconnect
  overwrites credentials in place; a delete orphans the phone-number
  mapping and forces a rebuild the refresh would have skipped.
* **Do not pick a second WABA to route around a selection error.**
  `WHATSAPP_INVALID_SELECTION` is the picker guarding the granted
  options — splitting sends across WABAs over one bad pair splits your
  quality rating too.
* **Do not loop `POST /whatsapp/connect` against `WHATSAPP_META_APP_DISABLED`.**
  The flag releases on Devotel's side; each retry returns the same code
  and burns your rate budget.
* **Do not escalate a selection error without the (waba\_id, phone\_number\_id)
  you sent.** Support reconstructs the picker from those ids — a report
  without them starts from nothing.

## Escalation payload

Worked the family and still red? Email
[whatsapp-support@devotel.io](mailto:whatsapp-support@devotel.io) with
these four items so the recovery can be traced without a back-and-forth:

1. The **exact error code** from the family above, with the timestamp of
   the failing call.
2. The **`waba_id` and `phone_number_id`** you sent — even on a failed
   selection, those ids name which pick failed.
3. The **current `GET /api/v1/whatsapp/status` payload** — it carries the
   `connected`, `status`, and `account_review_status` fields that decide
   which family member is live.
4. For `WHATSAPP_META_APP_DISABLED` only, the **status.orbit.devotel.io
   check** you saw at retry time, to confirm the flag is still open.

## See also

* [WhatsApp connection recovery](/troubleshooting/whatsapp-connection) —
  the send-side twins of this family, `WHATSAPP_NOT_CONNECTED` and
  `WHATSAPP_CONNECTION_INVALID`, plus tier limits and quality pauses.
* [WABA setup](/guides/whatsapp/waba-setup) — first-time Embedded
  Sign-up, approval timelines, and messaging tiers.
* [WABA migration](/guides/whatsapp/waba-migration) — move a WABA from
  another provider; the migration recovery page is
  [Troubleshooting: WhatsApp migration](/troubleshooting/whatsapp-migration).
* [Troubleshoot a WhatsApp template](/troubleshooting/whatsapp-template) —
  template-level failures once the connection is green.
* [Error codes](/reference/error-codes) — every `WHATSAPP_*` code the API
  returns, with its HTTP status.
