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 isPOST /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;
this page owns everything the connect flow itself returns.
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.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: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:
- Open a fresh Embedded Sign-up run from Settings → Channels → WhatsApp → Connect and finish the Meta dialog without pausing between steps.
- Send the new code immediately — the dashboard does this in one motion; over the API it is:
- Back to green: a
{"data": {"status": "connected"}}response, thenGET /api/v1/whatsapp/statusreturnsconnected: true.
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:
- 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.
- Back to green:
GET /api/v1/whatsapp/statusreturns the samewaba_id+phone_number_idpair as before, now withconnected: trueandstatus: "active". A template send to a known-good recipient completes the check.
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:
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.
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:
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 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
codeafter 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_SELECTIONis the picker guarding the granted options — splitting sends across WABAs over one bad pair splits your quality rating too. - Do not loop
POST /whatsapp/connectagainstWHATSAPP_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 with these four items so the recovery can be traced without a back-and-forth:- The exact error code from the family above, with the timestamp of the failing call.
- The
waba_idandphone_number_idyou sent — even on a failed selection, those ids name which pick failed. - The current
GET /api/v1/whatsapp/statuspayload — it carries theconnected,status, andaccount_review_statusfields that decide which family member is live. - For
WHATSAPP_META_APP_DISABLEDonly, the status.orbit.devotel.io check you saw at retry time, to confirm the flag is still open.
See also
- WhatsApp connection recovery —
the send-side twins of this family,
WHATSAPP_NOT_CONNECTEDandWHATSAPP_CONNECTION_INVALID, plus tier limits and quality pauses. - WABA setup — first-time Embedded Sign-up, approval timelines, and messaging tiers.
- WABA migration — move a WABA from another provider; the migration recovery page is Troubleshooting: WhatsApp migration.
- Troubleshoot a WhatsApp template — template-level failures once the connection is green.
- Error codes — every
WHATSAPP_*code the API returns, with its HTTP status.