{account.business_name || account.name}
+{account.last_error}
} +Loading WhatsApp setup…
+ async function reportSession(session: SignupSession, code: string | null) { + if (finishingRef.current) return + finishingRef.current = true + const terminal = session.event === 'CANCEL' || session.event === 'ERROR' + if (!terminal) setPhase('provisioning') + try { + const payload = await whatsappApi.completeSignup({ + event: session.event, + code, + waba_id: session.wabaId, + phone_number_id: session.phoneNumberId, + business_id: session.businessId, + error_message: session.errorMessage || session.currentStep, + }) + setResult(payload) + if (payload.accounts.length) { + setAccounts((current) => { + const incoming = new Map(payload.accounts.map((account) => [account.id, account])) + const merged = current.map((account) => incoming.get(account.id) || account) + for (const account of payload.accounts) { + if (!merged.some((item) => item.id === account.id)) merged.unshift(account) + } + return merged + }) + } + if (terminal) { + setNotice(payload.message) + setPhase(accountsRef.current.length || payload.accounts.length ? 'list' : 'landing') + } else { + setPhase('result') + } + } catch (reason) { + setError(reason instanceof Error ? reason.message : 'WhatsApp setup could not be completed') + setPhase(accountsRef.current.length ? 'list' : 'landing') + } finally { + finishingRef.current = false + codeRef.current = null + sessionRef.current = null + } + } + + reportRef.current = reportSession + finishRef.current = finishIfReady + + async function finishIfReady() { + const session = sessionRef.current + const code = codeRef.current + if (!session || !code || finishingRef.current) return + if (!session.event.startsWith('FINISH')) return + await reportSession(session, code) + } + + async function startEmbeddedSignup() { + setError('') + setNotice('') + setMenuId(null) + if (!config?.ready || !config.app_id || !config.config_id) { + setError('WhatsApp Embedded Signup is not configured yet. Add the Meta app id and Embedded Signup configuration id on the server.') + return + } + sessionRef.current = null + codeRef.current = null + finishingRef.current = false + setBusy(true) + try { + await loadFacebookSdk() + const sdk = facebookSdk() + if (!sdk) throw new Error('Meta sign-in did not initialize') + sdk.init({ + appId: config.app_id, + cookie: true, + xfbml: false, + version: config.api_version, + }) + sdk.login( + (response) => { + setBusy(false) + const code = response.authResponse?.code + if (!code) { + const session = sessionRef.current + if (session && (session.event === 'CANCEL' || session.event === 'ERROR')) return + setNotice('WhatsApp setup was closed before Meta returned an authorization. Nothing was connected.') + setPhase(accounts.length ? 'list' : 'landing') + return + } + codeRef.current = code + void finishIfReady() + window.setTimeout(() => { + if (codeRef.current !== code || finishingRef.current) return + if (!sessionRef.current) sessionRef.current = { event: 'FINISH' } + if (sessionRef.current.event.startsWith('FINISH')) void finishIfReady() + }, 1500) + }, + { + config_id: config.config_id, + response_type: 'code', + override_default_response_type: true, + extras: { + setup: {}, + sessionInfoVersion: '3', + }, + }, + ) + } catch (reason) { + setBusy(false) + setError(reason instanceof Error ? reason.message : 'Unable to open Meta Embedded Signup') + } + } + + async function refreshSelected() { + if (!selected) return + setBusy(true) + setError('') + try { + replaceAccount(await whatsappApi.refreshAccount(selected.id)) + } catch (reason) { + setError(reason instanceof Error ? reason.message : 'Unable to refresh this connection') + } finally { + setBusy(false) + } + } + + async function confirmDisconnect() { + if (!disconnecting) return + setBusy(true) + setError('') + try { + const next = await whatsappApi.disconnectAccount(disconnecting.id) + replaceAccount(next) + setDisconnecting(null) + setNotice('This WhatsApp account was disconnected. Existing Urmi records were kept. Reconnect to authorize it again.') + if (selectedId === next.id) setPhase('list') + } catch (reason) { + setError(reason instanceof Error ? reason.message : 'Unable to disconnect this account') + } finally { + setBusy(false) + } + } + + async function copyWebhook(url: string) { + await navigator.clipboard.writeText(url) + setCopied(true) + window.setTimeout(() => setCopied(false), 2000) + } + + const resultAccount = result?.accounts[0] return ( -Configure Meta webhooks so Urmi can receive messages and delivery updates on behalf of your customers.
-Loading WhatsApp Business…
} + + {phase !== 'loading' && phase !== 'detail' && ( ++ {accounts.length + ? 'Manage WhatsApp Business accounts and phone numbers connected to this Urmi workspace.' + : 'Connect and manage WhatsApp Business accounts with Urmi.'} +
+{error}
} + {notice &&{notice}
} - {setup && ( - <> -Paste these into App Dashboard → WhatsApp → Configuration → Configure Webhooks
-{setup.webhook_url}
- {setup.verify_token || 'Not configured'}
- Paste this exact value into Meta → Configure Webhooks → Verify token.
- In Meta, subscribe to these fields after verifying the callback URL.
-{field}Set up WhatsApp Business Platform through Meta's secure onboarding process.
+
- Minimum for messaging: messages. For a tech provider / BSP, also enable template and account fields.
-
Incoming payloads from Meta (after verification succeeds).
-No webhook events yet. Click Verify and save in Meta to send a test.
- ) : ( -We're configuring your WhatsApp Business connection. This screen updates from the setup result, not from a placeholder status.
+{result.message}
+ {resultAccount && ( +{account.last_error}
} +{selected.last_error}
} +Messaging stays unavailable until Urmi can verify a connected phone on this account.
+ )} +Meta has not returned a phone number for this account yet.
} +Loading templates…
} + {templates && !templatesAvailable &&Template access is unavailable until this account is authorized.
} + {templates && templatesAvailable && templates.length === 0 &&No templates were returned for this WhatsApp Business Account.
} + {templates && templates.length > 0 && ( +Webhook delivery for this workspace. Verification credentials stay on Urmi's servers.
+Loading configuration…
} + {developer && ( + <> +{developer.webhook_url}
+ {developer.verify_token_configured ? 'Hidden — managed by Urmi' : 'Not configured'}
+ The verify token is not shown in the customer console.
+ Webhook subscriptions
+Webhook status
Loading logs…
} + {logs && logs.audits.length === 0 &&No connection events yet.
} +No webhook events for this account.
} ++ Urmi will stop using this WhatsApp Business Account and clear its stored authorization. + The account record stays in this workspace so you can reconnect without starting over. +
+