No manual tokens
Users never see or paste access tokens — the exchange happens server-side.
Self-serve Meta onboarding — connect a WhatsApp Business Account via Facebook OAuth without copying tokens by hand
Embedded Signup is the self-serve way to connect a WhatsApp Business Account (WABA) to Soosh. Instead of manually copying a permanent access token, phone ID, and WABA ID out of the Meta dashboard, the user clicks a button, authenticates with Facebook in a popup, and Soosh does the token exchange, discovery, subscription, and registration for them.
Embedded Signup wraps Meta’s official onboarding flow (the Facebook JS SDK launching Meta’s Embedded Signup dialog). The user grants your Meta app access to their WhatsApp Business Account and phone number; the SDK hands back a short-lived authorization code; Soosh exchanges it for a permanent access token, discovers the WABA and phone number behind it, subscribes your app to the account’s webhooks, and (for most numbers) registers the phone — all in one round trip.
No manual tokens
Users never see or paste access tokens — the exchange happens server-side.
Auto-discovery
WABA ID and phone number are discovered from the token if the SDK didn’t supply them.
Encrypted at rest
The permanent token, app secret, and PIN are AES-encrypted before being saved.
Coexistence
SMB numbers still running on the WhatsApp Business mobile app connect without registration.
Embedded Signup needs Meta app credentials configured in the [whatsapp] section of config.toml:
[whatsapp]webhook_verify_token = "random-string-change-this"app_id = "" # Meta App ID for Embedded Signupapp_secret = "" # Meta App Secretconfig_id = "" # WhatsApp Config ID for frontend login| Setting | Purpose |
|---|---|
app_id |
Your Meta App ID. Sent to the frontend so the Facebook JS SDK can launch the dialog. |
config_id |
The Facebook Login for Business configuration ID (WhatsApp Embedded Signup variation). The configuration, not the code, selects the Embedded Signup version and products; create it with products selected to get v4, since v2 and v3 stop working on October 15, 2026. |
app_secret |
Your Meta App Secret. Used server-side to exchange the code for a token and to debug the token during discovery. Never sent to the frontend. |
webhook_verify_token |
Default verify token for the /api/webhook endpoint; a per-account token is generated if you don’t pass one. |
See Configuration for the full [whatsapp] block and environment-variable overrides.
The frontend fetches the resolved (non-secret) values before launching the dialog:
GET /api/embedded-signup/config→ { "whatsapp_app_id": "…", "whatsapp_config_id": "…", "whatsapp_api_version": "v21.0" }The app secret is deliberately not included in this response.
Launch the dialog. The frontend loads the Facebook JS SDK with the app_id and config_id from /api/embedded-signup/config and opens Meta’s Embedded Signup dialog. The user selects (or creates) their WhatsApp Business Account and phone number and grants your app permission.
SDK returns a code. On success the SDK returns a short-lived authorization code (and, usually, the phone_id and waba_id).
Exchange the code. The frontend posts it to the backend:
POST /api/accounts/exchange-token{ "code": "…", "phone_id": "…optional…", "waba_id": "…optional…", "name": "…optional…", "webhook_verify_token": "…optional…"}The server then:
whatsapp_business_management granular scopes for the WABA ID (falling back to shared-WABA lookup), then lists the WABA’s phone numbers and picks the first one.Response. On success the account is returned; if it went active via registration the generated pin is included, and if registration failed a warning is returned so the user can register manually.
Both GET /api/embedded-signup/config and POST /api/accounts/exchange-token require an authenticated session; the exchange additionally requires accounts:write.
Cloud API phone numbers must be registered with a 6-digit two-step-verification PIN before they can send. Exchange-token attempts this automatically, but if it failed (the account comes back with a warning and a pending_registration status) you can register it explicitly:
POST /api/accounts/{id}/register{ "pin": "…optional 6-digit PIN…" }If you omit the PIN, the server generates a secure random one. On success the account status flips to active and the PIN is returned so you can store it. This endpoint also requires accounts:write.
Coexistence lets a number that’s still being used in the WhatsApp Business mobile app connect to the Cloud API at the same time — the small-business owner keeps chatting from their phone while Soosh mirrors those conversations.
During exchange-token, Soosh inspects the phone number info from Meta and flags the account IsSMB when the number is on the Business App (is_on_biz_app) or its platform type is SMB / SMB_CLOUD_API. For these numbers:
POST /api/accounts/{id}/register set the account straight to active with no PIN.POST /{phone-number-id}/smb_app_data, once each). Meta only allows this within 24 hours of onboarding; after that the business has to disconnect and sign up again. Contacts arrive as smb_app_state_sync webhooks and history as history webhooks. History is stored with its original times and doesn’t trigger chatbot or AI replies, notifications, or outgoing webhooks. If the sync can’t start, the signup response carries a warning and the Accounts page shows it.smb_message_echoes), and vice versa.account_update with ACCOUNT_OFFBOARDED and the account shows as disconnected; ACCOUNT_RECONNECTED makes it active again. PARTNER_REMOVED and ACCOUNT_DELETED also mark it disconnected.[whatsapp] config block and env overrides