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}
}