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 fsGroup: 1000
containers: containers:
- name: hermes-mcp - name: hermes-mcp
image: localhost:32000/hermes-mcp@sha256:6685df4c86cceeaeb645c9ccee32f9396915a7c30e57f685945056c92516723d image: localhost:32000/hermes-mcp@sha256:0132eade2173eae3428a24757d2228253acf3f924f7a6c7a061f088e8d0dc891
imagePullPolicy: Always imagePullPolicy: Always
securityContext: securityContext:
allowPrivilegeEscalation: false allowPrivilegeEscalation: false

View File

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