fix(whatsapp): fall back to default Twilio account when customer has no WhatsApp creds

Customers who haven't connected WhatsApp in the dashboard now
automatically use the SquareMCP default number (+19547385805 via Twilio)
instead of throwing 'WhatsApp not connected'. Routing flag usingDefault
replaces the !customer check so OAuth sessions get the same Twilio path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Garfield
2026-05-17 00:46:21 -04:00
parent c270f8f74b
commit 4746c4ee1c
2 changed files with 17 additions and 13 deletions

View File

@@ -22,7 +22,7 @@ spec:
fsGroup: 1000
containers:
- name: hermes-mcp
image: localhost:32000/hermes-mcp@sha256:6685df4c86cceeaeb645c9ccee32f9396915a7c30e57f685945056c92516723d
image: localhost:32000/hermes-mcp@sha256:0132eade2173eae3428a24757d2228253acf3f924f7a6c7a061f088e8d0dc891
imagePullPolicy: Always
securityContext:
allowPrivilegeEscalation: false

View File

@@ -113,15 +113,19 @@ async function whatsappApiRequest(
async function resolveWhatsAppCreds(
args: { account?: string },
customer?: Customer
): Promise<{ phoneId: string; accessToken: string; businessAccountId: string }> {
): Promise<{ phoneId: string; accessToken: string; businessAccountId: string; usingDefault: boolean }> {
if (customer) {
const creds = await customer.getCredential<WhatsAppCredentials>('whatsapp');
if (!creds) throw new Error('WhatsApp not connected for this account');
return {
phoneId: creds.phoneNumberId,
accessToken: creds.accessToken,
businessAccountId: creds.businessAccountId,
};
if (creds) {
return {
phoneId: creds.phoneNumberId,
accessToken: creds.accessToken,
businessAccountId: creds.businessAccountId,
usingDefault: false,
};
}
// No WhatsApp connected — fall back to default env var account (Twilio-routed)
console.log(`[whatsapp] customer ${customer.id} has no WhatsApp creds, falling back to default`);
}
const account = args.account ?? 'default';
const phoneId = getPhoneNumberId(account);
@@ -130,7 +134,7 @@ async function resolveWhatsAppCreds(
if (!phoneId || !accessToken) {
throw new Error('Missing WhatsApp credentials. Set WHATSAPP_{ACCOUNT}_PHONE_NUMBER_ID and WHATSAPP_{ACCOUNT}_ACCESS_TOKEN');
}
return { phoneId, accessToken, businessAccountId };
return { phoneId, accessToken, businessAccountId, usingDefault: true };
}
export async function sendMessage(
@@ -140,7 +144,7 @@ export async function sendMessage(
const audit = customer ? createToolAudit(customer.id, 'whatsapp:sendMessage') : null;
const auditArgs = { to: args.to };
const { phoneId, accessToken } = await resolveWhatsAppCreds(args, customer);
const { phoneId, accessToken, usingDefault } = await resolveWhatsAppCreds(args, customer);
const body = {
messaging_product: 'whatsapp',
@@ -151,7 +155,7 @@ export async function sendMessage(
try {
let result: { success: boolean; message_id: string };
if (!customer && isTwilioAccount(args.account ?? 'default')) {
if (usingDefault && isTwilioAccount('default')) {
result = await twilioSend(args.to, args.message);
} else {
const data = await whatsappApiRequest(phoneId, accessToken, 'messages', 'POST', body);
@@ -173,7 +177,7 @@ export async function sendTemplate(
const audit = customer ? createToolAudit(customer.id, 'whatsapp:sendTemplate') : null;
const auditArgs = { to: args.to, template_name: args.template_name };
const { phoneId, accessToken, businessAccountId } = await resolveWhatsAppCreds(args, customer);
const { phoneId, accessToken, businessAccountId, usingDefault } = await resolveWhatsAppCreds(args, customer);
const body: Record<string, unknown> = {
messaging_product: 'whatsapp',
@@ -191,7 +195,7 @@ export async function sendTemplate(
try {
let result: { success: boolean; message_id: string };
if (!customer && isTwilioAccount(args.account ?? 'default')) {
if (usingDefault && isTwilioAccount('default')) {
const text = await resolveTemplateText(
businessAccountId,
accessToken,