From 706edf5f442cb485ceda24d617c4e5872ab752e8 Mon Sep 17 00:00:00 2001 From: YellowSnnowmann <167776381+YellowSnnowmann@users.noreply.github.com> Date: Wed, 13 May 2026 08:21:54 +0530 Subject: [PATCH] fix(composio): collect WABA ID before WhatsApp Business OAuth flow (#1550) Co-authored-by: Claude Sonnet 4.6 Co-authored-by: Steven Enamakel --- .../composio/ComposioConnectModal.tsx | 43 +++++++++++- app/src/lib/composio/composioApi.ts | 10 ++- src/openhuman/composio/client.rs | 32 ++++++++- src/openhuman/composio/client_tests.rs | 66 ++++++++++++++++++- src/openhuman/composio/ops.rs | 5 +- src/openhuman/composio/ops_tests.rs | 6 +- src/openhuman/composio/schemas.rs | 25 +++++-- src/openhuman/composio/tools.rs | 2 +- 8 files changed, 167 insertions(+), 22 deletions(-) diff --git a/app/src/components/composio/ComposioConnectModal.tsx b/app/src/components/composio/ComposioConnectModal.tsx index 846c5072b..8ae03d4bd 100644 --- a/app/src/components/composio/ComposioConnectModal.tsx +++ b/app/src/components/composio/ComposioConnectModal.tsx @@ -13,7 +13,7 @@ * keep the Skills page badge in sync too, so the card reflects the new * state as soon as the modal closes. */ -import { useCallback, useEffect, useRef, useState } from 'react'; +import { type ChangeEvent, useCallback, useEffect, useRef, useState } from 'react'; import { createPortal } from 'react-dom'; import { @@ -73,6 +73,9 @@ export default function ComposioConnectModal({ ); const [error, setError] = useState(null); const [connectUrl, setConnectUrl] = useState(null); + // WhatsApp Business requires a WABA ID before the OAuth flow can start. + const [wabaId, setWabaId] = useState(''); + const needsWabaId = toolkit.slug === 'whatsapp'; const [activeConnection, setActiveConnection] = useState( connection ); @@ -188,11 +191,16 @@ export default function ComposioConnectModal({ }, []); const handleConnect = useCallback(async () => { + if (needsWabaId && !wabaId.trim()) { + setError('Please enter your WhatsApp Business Account ID (WABA ID) to continue.'); + return; + } setPhase('authorizing'); setError(null); setConnectUrl(null); try { - const resp = await authorize(toolkit.slug); + const extraParams = needsWabaId ? { waba_id: wabaId.trim() } : undefined; + const resp = await authorize(toolkit.slug, extraParams); setConnectUrl(resp.connectUrl); await openUrl(resp.connectUrl); setPhase('waiting'); @@ -202,7 +210,7 @@ export default function ComposioConnectModal({ setPhase('error'); setError(`Authorization failed: ${msg}`); } - }, [startPolling, toolkit.slug]); + }, [needsWabaId, startPolling, toolkit.slug, wabaId]); // Fetch the stored scope pref whenever the modal lands in the // 'connected' phase. Re-fetching each time we transition (rather @@ -360,6 +368,35 @@ export default function ComposioConnectModal({ admin toggles.

+ {needsWabaId && ( +
+ + ) => { + setWabaId(e.target.value); + if (error) setError(null); + }} + placeholder="e.g. 123456789012345" + className="w-full rounded-xl border border-stone-200 bg-white px-3 py-2 text-sm text-stone-900 placeholder:text-stone-400 focus:border-primary-400 focus:outline-none focus:ring-2 focus:ring-primary-100" + /> +

+ Find it via GET /me/businesses then{' '} + + GET /{business_id}/owned_whatsapp_business_accounts + {' '} + using your Meta access token. +

+
+ )} + {error && phase === 'idle' &&

{error}

}