From 82f82faba4169126716c5d7801561fbe8728a00c Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Mon, 17 Nov 2025 15:34:51 -0800 Subject: [PATCH 1/8] fix(triggers): dedup + not surfacing deployment status log --- .../app/api/webhooks/trigger/[path]/route.ts | 30 +------ apps/sim/background/webhook-execution.ts | 4 +- apps/sim/lib/idempotency/service.ts | 32 ++++++- apps/sim/lib/webhooks/provider-utils.ts | 85 +++++++++++++++++++ 4 files changed, 120 insertions(+), 31 deletions(-) create mode 100644 apps/sim/lib/webhooks/provider-utils.ts diff --git a/apps/sim/app/api/webhooks/trigger/[path]/route.ts b/apps/sim/app/api/webhooks/trigger/[path]/route.ts index 4f8704489d..0de3c1a632 100644 --- a/apps/sim/app/api/webhooks/trigger/[path]/route.ts +++ b/apps/sim/app/api/webhooks/trigger/[path]/route.ts @@ -1,7 +1,5 @@ import { type NextRequest, NextResponse } from 'next/server' -import { v4 as uuidv4 } from 'uuid' import { createLogger } from '@/lib/logs/console/logger' -import { LoggingSession } from '@/lib/logs/execution/logging-session' import { generateRequestId } from '@/lib/utils' import { checkRateLimits, @@ -139,34 +137,10 @@ export async function POST( if (foundWebhook.blockId) { const blockExists = await blockExistsInDeployment(foundWorkflow.id, foundWebhook.blockId) if (!blockExists) { - logger.warn( + logger.info( `[${requestId}] Trigger block ${foundWebhook.blockId} not found in deployment for workflow ${foundWorkflow.id}` ) - - const executionId = uuidv4() - const loggingSession = new LoggingSession(foundWorkflow.id, executionId, 'webhook', requestId) - - const actorUserId = foundWorkflow.workspaceId - ? (await import('@/lib/workspaces/utils')).getWorkspaceBilledAccountUserId( - foundWorkflow.workspaceId - ) || foundWorkflow.userId - : foundWorkflow.userId - - await loggingSession.safeStart({ - userId: actorUserId, - workspaceId: foundWorkflow.workspaceId || '', - variables: {}, - }) - - await loggingSession.safeCompleteWithError({ - error: { - message: `Trigger block not deployed. The webhook trigger (block ${foundWebhook.blockId}) is not present in the deployed workflow. Please redeploy the workflow.`, - stackTrace: undefined, - }, - traceSpans: [], - }) - - return new NextResponse('Trigger block not deployed', { status: 404 }) + return new NextResponse('Trigger block not found in deployment', { status: 404 }) } } diff --git a/apps/sim/background/webhook-execution.ts b/apps/sim/background/webhook-execution.ts index 14e8ca40e2..7698616b59 100644 --- a/apps/sim/background/webhook-execution.ts +++ b/apps/sim/background/webhook-execution.ts @@ -112,7 +112,9 @@ export async function executeWebhookJob(payload: WebhookExecutionPayload) { const idempotencyKey = IdempotencyService.createWebhookIdempotencyKey( payload.webhookId, - payload.headers + payload.headers, + payload.body, + payload.provider ) const runOperation = async () => { diff --git a/apps/sim/lib/idempotency/service.ts b/apps/sim/lib/idempotency/service.ts index e20dc6dbfe..31495c2a99 100644 --- a/apps/sim/lib/idempotency/service.ts +++ b/apps/sim/lib/idempotency/service.ts @@ -4,6 +4,7 @@ import { idempotencyKey } from '@sim/db/schema' import { and, eq } from 'drizzle-orm' import { createLogger } from '@/lib/logs/console/logger' import { getRedisClient } from '@/lib/redis' +import { extractProviderIdentifierFromBody } from '@/lib/webhooks/provider-utils' const logger = createLogger('IdempotencyService') @@ -451,13 +452,25 @@ export class IdempotencyService { /** * Create an idempotency key from a webhook payload following RFC best practices - * Standard webhook headers (webhook-id, x-webhook-id, etc.) + * Checks both headers and body for unique identifiers to prevent duplicate executions + * + * @param webhookId - The webhook database ID + * @param headers - HTTP headers from the webhook request + * @param body - Parsed webhook body (optional, used for provider-specific identifiers) + * @param provider - Provider name for body extraction (optional) + * @returns A unique idempotency key for this webhook event */ - static createWebhookIdempotencyKey(webhookId: string, headers?: Record): string { + static createWebhookIdempotencyKey( + webhookId: string, + headers?: Record, + body?: any, + provider?: string + ): string { const normalizedHeaders = headers ? Object.fromEntries(Object.entries(headers).map(([k, v]) => [k.toLowerCase(), v])) : undefined + // Check standard webhook headers first const webhookIdHeader = normalizedHeaders?.['webhook-id'] || normalizedHeaders?.['x-webhook-id'] || @@ -470,7 +483,22 @@ export class IdempotencyService { return `${webhookId}:${webhookIdHeader}` } + // Check body for provider-specific unique identifiers + if (body && provider) { + const bodyIdentifier = extractProviderIdentifierFromBody(provider, body) + + if (bodyIdentifier) { + return `${webhookId}:${bodyIdentifier}` + } + } + + // No unique identifier found - generate random UUID + // This means duplicate detection will not work for this webhook const uniqueId = randomUUID() + logger.warn('No unique identifier found, duplicate executions may occur', { + webhookId, + provider, + }) return `${webhookId}:${uniqueId}` } } diff --git a/apps/sim/lib/webhooks/provider-utils.ts b/apps/sim/lib/webhooks/provider-utils.ts new file mode 100644 index 0000000000..4c94335cb9 --- /dev/null +++ b/apps/sim/lib/webhooks/provider-utils.ts @@ -0,0 +1,85 @@ +/** + * Provider-specific unique identifier extractors for webhook idempotency + */ + +function extractSlackIdentifier(body: any): string | null { + if (body.event_id) { + return body.event_id + } + + if (body.event?.ts && body.team_id) { + return `${body.team_id}:${body.event.ts}` + } + + return null +} + +function extractTwilioIdentifier(body: any): string | null { + return body.MessageSid || body.CallSid || null +} + +function extractStripeIdentifier(body: any): string | null { + if (body.id && body.object === 'event') { + return body.id + } + return null +} + +function extractHubSpotIdentifier(body: any): string | null { + if (Array.isArray(body) && body.length > 0 && body[0]?.eventId) { + return String(body[0].eventId) + } + return null +} + +function extractLinearIdentifier(body: any): string | null { + if (body.action && body.data?.id) { + return `${body.action}:${body.data.id}` + } + return null +} + +function extractJiraIdentifier(body: any): string | null { + if (body.webhookEvent && (body.issue?.id || body.project?.id)) { + return `${body.webhookEvent}:${body.issue?.id || body.project?.id}` + } + return null +} + +function extractMicrosoftTeamsIdentifier(body: any): string | null { + if (body.value && Array.isArray(body.value) && body.value.length > 0) { + const notification = body.value[0] + if (notification.subscriptionId && notification.resourceData?.id) { + return `${notification.subscriptionId}:${notification.resourceData.id}` + } + } + return null +} + +function extractAirtableIdentifier(body: any): string | null { + if (body.cursor && typeof body.cursor === 'string') { + return body.cursor + } + return null +} + +const PROVIDER_EXTRACTORS: Record string | null> = { + slack: extractSlackIdentifier, + twilio: extractTwilioIdentifier, + twilio_voice: extractTwilioIdentifier, + stripe: extractStripeIdentifier, + hubspot: extractHubSpotIdentifier, + linear: extractLinearIdentifier, + jira: extractJiraIdentifier, + microsoftteams: extractMicrosoftTeamsIdentifier, + airtable: extractAirtableIdentifier, +} + +export function extractProviderIdentifierFromBody(provider: string, body: any): string | null { + if (!body || typeof body !== 'object') { + return null + } + + const extractor = PROVIDER_EXTRACTORS[provider] + return extractor ? extractor(body) : null +} From 1cc6f54ad483c0fb0699ccdbfe0f01fd25c17bdf Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Mon, 17 Nov 2025 16:02:35 -0800 Subject: [PATCH 2/8] fix ms teams --- apps/sim/app/api/cron/renew-subscriptions/route.ts | 2 +- apps/sim/app/api/webhooks/route.ts | 4 ++-- apps/sim/app/api/webhooks/test/route.ts | 2 +- .../sim/executor/handlers/trigger/trigger-handler.ts | 2 +- apps/sim/lib/webhooks/processor.ts | 12 ++++++------ apps/sim/lib/webhooks/provider-utils.ts | 2 +- apps/sim/lib/webhooks/utils.server.ts | 12 ++++++------ apps/sim/lib/webhooks/webhook-helpers.ts | 2 +- 8 files changed, 19 insertions(+), 19 deletions(-) diff --git a/apps/sim/app/api/cron/renew-subscriptions/route.ts b/apps/sim/app/api/cron/renew-subscriptions/route.ts index 2f58f5faa3..214399345a 100644 --- a/apps/sim/app/api/cron/renew-subscriptions/route.ts +++ b/apps/sim/app/api/cron/renew-subscriptions/route.ts @@ -35,7 +35,7 @@ export async function GET(request: NextRequest) { }) .from(webhookTable) .innerJoin(workflowTable, eq(webhookTable.workflowId, workflowTable.id)) - .where(and(eq(webhookTable.isActive, true), eq(webhookTable.provider, 'microsoftteams'))) + .where(and(eq(webhookTable.isActive, true), eq(webhookTable.provider, 'microsoft-teams'))) logger.info( `Found ${webhooksWithWorkflows.length} active Teams webhooks, checking for expiring subscriptions` diff --git a/apps/sim/app/api/webhooks/route.ts b/apps/sim/app/api/webhooks/route.ts index 86fd3ff644..5f0d58a27e 100644 --- a/apps/sim/app/api/webhooks/route.ts +++ b/apps/sim/app/api/webhooks/route.ts @@ -137,7 +137,7 @@ export async function POST(request: NextRequest) { const isCredentialBased = credentialBasedProviders.includes(provider) // Treat Microsoft Teams chat subscription as credential-based for path generation purposes const isMicrosoftTeamsChatSubscription = - provider === 'microsoftteams' && + provider === 'microsoft-teams' && typeof providerConfig === 'object' && providerConfig?.triggerId === 'microsoftteams_chat_subscription' @@ -297,7 +297,7 @@ export async function POST(request: NextRequest) { } } - if (provider === 'microsoftteams') { + if (provider === 'microsoft-teams') { const { createTeamsSubscription } = await import('@/lib/webhooks/webhook-helpers') logger.info(`[${requestId}] Creating Teams subscription before saving to database`) try { diff --git a/apps/sim/app/api/webhooks/test/route.ts b/apps/sim/app/api/webhooks/test/route.ts index 8467f53063..3de2e19b1b 100644 --- a/apps/sim/app/api/webhooks/test/route.ts +++ b/apps/sim/app/api/webhooks/test/route.ts @@ -441,7 +441,7 @@ export async function GET(request: NextRequest) { }) } - case 'microsoftteams': { + case 'microsoft-teams': { const hmacSecret = providerConfig.hmacSecret if (!hmacSecret) { diff --git a/apps/sim/executor/handlers/trigger/trigger-handler.ts b/apps/sim/executor/handlers/trigger/trigger-handler.ts index 85fbffb4ab..1b53cd8641 100644 --- a/apps/sim/executor/handlers/trigger/trigger-handler.ts +++ b/apps/sim/executor/handlers/trigger/trigger-handler.ts @@ -55,7 +55,7 @@ export class TriggerBlockHandler implements BlockHandler { } } - if (provider === 'microsoftteams') { + if (provider === 'microsoft-teams') { const providerData = (starterOutput as any)[provider] || webhookData[provider] || {} const payloadSource = providerData?.message?.raw || webhookData.payload || {} return { diff --git a/apps/sim/lib/webhooks/processor.ts b/apps/sim/lib/webhooks/processor.ts index 1b76610096..5d58b36d06 100644 --- a/apps/sim/lib/webhooks/processor.ts +++ b/apps/sim/lib/webhooks/processor.ts @@ -250,7 +250,7 @@ export async function verifyProviderAuth( const rawProviderConfig = (foundWebhook.providerConfig as Record) || {} const providerConfig = resolveProviderConfigEnvVars(rawProviderConfig, decryptedEnvVars) - if (foundWebhook.provider === 'microsoftteams') { + if (foundWebhook.provider === 'microsoft-teams') { if (providerConfig.hmacSecret) { const authHeader = request.headers.get('authorization') @@ -556,7 +556,7 @@ export async function checkRateLimits( traceSpans: [], }) - if (foundWebhook.provider === 'microsoftteams') { + if (foundWebhook.provider === 'microsoft-teams') { return NextResponse.json( { type: 'message', @@ -634,7 +634,7 @@ export async function checkUsageLimits( traceSpans: [], }) - if (foundWebhook.provider === 'microsoftteams') { + if (foundWebhook.provider === 'microsoft-teams') { return NextResponse.json( { type: 'message', @@ -783,7 +783,7 @@ export async function queueWebhookExecution( // For Microsoft Teams Graph notifications, extract unique identifiers for idempotency if ( - foundWebhook.provider === 'microsoftteams' && + foundWebhook.provider === 'microsoft-teams' && body?.value && Array.isArray(body.value) && body.value.length > 0 @@ -835,7 +835,7 @@ export async function queueWebhookExecution( ) } - if (foundWebhook.provider === 'microsoftteams') { + if (foundWebhook.provider === 'microsoft-teams') { const providerConfig = (foundWebhook.providerConfig as Record) || {} const triggerId = providerConfig.triggerId as string | undefined @@ -886,7 +886,7 @@ export async function queueWebhookExecution( } catch (error: any) { logger.error(`[${options.requestId}] Failed to queue webhook execution:`, error) - if (foundWebhook.provider === 'microsoftteams') { + if (foundWebhook.provider === 'microsoft-teams') { return NextResponse.json( { type: 'message', diff --git a/apps/sim/lib/webhooks/provider-utils.ts b/apps/sim/lib/webhooks/provider-utils.ts index 4c94335cb9..2ae0681e94 100644 --- a/apps/sim/lib/webhooks/provider-utils.ts +++ b/apps/sim/lib/webhooks/provider-utils.ts @@ -71,7 +71,7 @@ const PROVIDER_EXTRACTORS: Record string | null> = { hubspot: extractHubSpotIdentifier, linear: extractLinearIdentifier, jira: extractJiraIdentifier, - microsoftteams: extractMicrosoftTeamsIdentifier, + 'microsoft-teams': extractMicrosoftTeamsIdentifier, airtable: extractAirtableIdentifier, } diff --git a/apps/sim/lib/webhooks/utils.server.ts b/apps/sim/lib/webhooks/utils.server.ts index d21867e189..47aacdba32 100644 --- a/apps/sim/lib/webhooks/utils.server.ts +++ b/apps/sim/lib/webhooks/utils.server.ts @@ -133,7 +133,7 @@ async function formatTeamsGraphNotification( input: 'Teams notification received', webhook: { data: { - provider: 'microsoftteams', + provider: 'microsoft-teams', path: foundWebhook?.path || '', providerConfig: foundWebhook?.providerConfig || {}, payload: body, @@ -397,7 +397,7 @@ async function formatTeamsGraphNotification( }, webhook: { data: { - provider: 'microsoftteams', + provider: 'microsoft-teams', path: foundWebhook?.path || '', providerConfig: foundWebhook?.providerConfig || {}, payload: body, @@ -446,7 +446,7 @@ async function formatTeamsGraphNotification( }, webhook: { data: { - provider: 'microsoftteams', + provider: 'microsoft-teams', path: foundWebhook?.path || '', providerConfig: foundWebhook?.providerConfig || {}, payload: body, @@ -818,7 +818,7 @@ export async function formatWebhookInput( } } - if (foundWebhook.provider === 'microsoftteams') { + if (foundWebhook.provider === 'microsoft-teams') { // Check if this is a Microsoft Graph change notification if (body?.value && Array.isArray(body.value) && body.value.length > 0) { return await formatTeamsGraphNotification(body, foundWebhook, foundWorkflow, request) @@ -875,7 +875,7 @@ export async function formatWebhookInput( webhook: { data: { - provider: 'microsoftteams', + provider: 'microsoft-teams', path: foundWebhook.path, providerConfig: foundWebhook.providerConfig, payload: body, @@ -1653,7 +1653,7 @@ export function verifyProviderWebhook( break } - case 'microsoftteams': + case 'microsoft-teams': break case 'generic': if (providerConfig.requireAuth) { diff --git a/apps/sim/lib/webhooks/webhook-helpers.ts b/apps/sim/lib/webhooks/webhook-helpers.ts index c1ccf976b7..9d9d6775a1 100644 --- a/apps/sim/lib/webhooks/webhook-helpers.ts +++ b/apps/sim/lib/webhooks/webhook-helpers.ts @@ -623,7 +623,7 @@ export async function cleanupExternalWebhook( ): Promise { if (webhook.provider === 'airtable') { await deleteAirtableWebhook(webhook, workflow, requestId) - } else if (webhook.provider === 'microsoftteams') { + } else if (webhook.provider === 'microsoft-teams') { await deleteTeamsSubscription(webhook, workflow, requestId) } else if (webhook.provider === 'telegram') { await deleteTelegramWebhook(webhook, requestId) From 217f808641acf16cd64eae2ba7a2d6aee72f500b Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Mon, 17 Nov 2025 16:08:03 -0800 Subject: [PATCH 3/8] change to microsoftteams --- apps/sim/app/api/cron/renew-subscriptions/route.ts | 2 +- apps/sim/app/api/webhooks/route.ts | 4 ++-- apps/sim/app/api/webhooks/test/route.ts | 2 +- .../sim/executor/handlers/trigger/trigger-handler.ts | 2 +- apps/sim/lib/webhooks/processor.ts | 12 ++++++------ apps/sim/lib/webhooks/provider-utils.ts | 2 +- apps/sim/lib/webhooks/utils.server.ts | 12 ++++++------ apps/sim/lib/webhooks/webhook-helpers.ts | 2 +- apps/sim/triggers/microsoftteams/chat_webhook.ts | 4 ++-- apps/sim/triggers/microsoftteams/webhook.ts | 2 +- 10 files changed, 22 insertions(+), 22 deletions(-) diff --git a/apps/sim/app/api/cron/renew-subscriptions/route.ts b/apps/sim/app/api/cron/renew-subscriptions/route.ts index 214399345a..2f58f5faa3 100644 --- a/apps/sim/app/api/cron/renew-subscriptions/route.ts +++ b/apps/sim/app/api/cron/renew-subscriptions/route.ts @@ -35,7 +35,7 @@ export async function GET(request: NextRequest) { }) .from(webhookTable) .innerJoin(workflowTable, eq(webhookTable.workflowId, workflowTable.id)) - .where(and(eq(webhookTable.isActive, true), eq(webhookTable.provider, 'microsoft-teams'))) + .where(and(eq(webhookTable.isActive, true), eq(webhookTable.provider, 'microsoftteams'))) logger.info( `Found ${webhooksWithWorkflows.length} active Teams webhooks, checking for expiring subscriptions` diff --git a/apps/sim/app/api/webhooks/route.ts b/apps/sim/app/api/webhooks/route.ts index 5f0d58a27e..86fd3ff644 100644 --- a/apps/sim/app/api/webhooks/route.ts +++ b/apps/sim/app/api/webhooks/route.ts @@ -137,7 +137,7 @@ export async function POST(request: NextRequest) { const isCredentialBased = credentialBasedProviders.includes(provider) // Treat Microsoft Teams chat subscription as credential-based for path generation purposes const isMicrosoftTeamsChatSubscription = - provider === 'microsoft-teams' && + provider === 'microsoftteams' && typeof providerConfig === 'object' && providerConfig?.triggerId === 'microsoftteams_chat_subscription' @@ -297,7 +297,7 @@ export async function POST(request: NextRequest) { } } - if (provider === 'microsoft-teams') { + if (provider === 'microsoftteams') { const { createTeamsSubscription } = await import('@/lib/webhooks/webhook-helpers') logger.info(`[${requestId}] Creating Teams subscription before saving to database`) try { diff --git a/apps/sim/app/api/webhooks/test/route.ts b/apps/sim/app/api/webhooks/test/route.ts index 3de2e19b1b..8467f53063 100644 --- a/apps/sim/app/api/webhooks/test/route.ts +++ b/apps/sim/app/api/webhooks/test/route.ts @@ -441,7 +441,7 @@ export async function GET(request: NextRequest) { }) } - case 'microsoft-teams': { + case 'microsoftteams': { const hmacSecret = providerConfig.hmacSecret if (!hmacSecret) { diff --git a/apps/sim/executor/handlers/trigger/trigger-handler.ts b/apps/sim/executor/handlers/trigger/trigger-handler.ts index 1b53cd8641..85fbffb4ab 100644 --- a/apps/sim/executor/handlers/trigger/trigger-handler.ts +++ b/apps/sim/executor/handlers/trigger/trigger-handler.ts @@ -55,7 +55,7 @@ export class TriggerBlockHandler implements BlockHandler { } } - if (provider === 'microsoft-teams') { + if (provider === 'microsoftteams') { const providerData = (starterOutput as any)[provider] || webhookData[provider] || {} const payloadSource = providerData?.message?.raw || webhookData.payload || {} return { diff --git a/apps/sim/lib/webhooks/processor.ts b/apps/sim/lib/webhooks/processor.ts index 5d58b36d06..1b76610096 100644 --- a/apps/sim/lib/webhooks/processor.ts +++ b/apps/sim/lib/webhooks/processor.ts @@ -250,7 +250,7 @@ export async function verifyProviderAuth( const rawProviderConfig = (foundWebhook.providerConfig as Record) || {} const providerConfig = resolveProviderConfigEnvVars(rawProviderConfig, decryptedEnvVars) - if (foundWebhook.provider === 'microsoft-teams') { + if (foundWebhook.provider === 'microsoftteams') { if (providerConfig.hmacSecret) { const authHeader = request.headers.get('authorization') @@ -556,7 +556,7 @@ export async function checkRateLimits( traceSpans: [], }) - if (foundWebhook.provider === 'microsoft-teams') { + if (foundWebhook.provider === 'microsoftteams') { return NextResponse.json( { type: 'message', @@ -634,7 +634,7 @@ export async function checkUsageLimits( traceSpans: [], }) - if (foundWebhook.provider === 'microsoft-teams') { + if (foundWebhook.provider === 'microsoftteams') { return NextResponse.json( { type: 'message', @@ -783,7 +783,7 @@ export async function queueWebhookExecution( // For Microsoft Teams Graph notifications, extract unique identifiers for idempotency if ( - foundWebhook.provider === 'microsoft-teams' && + foundWebhook.provider === 'microsoftteams' && body?.value && Array.isArray(body.value) && body.value.length > 0 @@ -835,7 +835,7 @@ export async function queueWebhookExecution( ) } - if (foundWebhook.provider === 'microsoft-teams') { + if (foundWebhook.provider === 'microsoftteams') { const providerConfig = (foundWebhook.providerConfig as Record) || {} const triggerId = providerConfig.triggerId as string | undefined @@ -886,7 +886,7 @@ export async function queueWebhookExecution( } catch (error: any) { logger.error(`[${options.requestId}] Failed to queue webhook execution:`, error) - if (foundWebhook.provider === 'microsoft-teams') { + if (foundWebhook.provider === 'microsoftteams') { return NextResponse.json( { type: 'message', diff --git a/apps/sim/lib/webhooks/provider-utils.ts b/apps/sim/lib/webhooks/provider-utils.ts index 2ae0681e94..4c94335cb9 100644 --- a/apps/sim/lib/webhooks/provider-utils.ts +++ b/apps/sim/lib/webhooks/provider-utils.ts @@ -71,7 +71,7 @@ const PROVIDER_EXTRACTORS: Record string | null> = { hubspot: extractHubSpotIdentifier, linear: extractLinearIdentifier, jira: extractJiraIdentifier, - 'microsoft-teams': extractMicrosoftTeamsIdentifier, + microsoftteams: extractMicrosoftTeamsIdentifier, airtable: extractAirtableIdentifier, } diff --git a/apps/sim/lib/webhooks/utils.server.ts b/apps/sim/lib/webhooks/utils.server.ts index 47aacdba32..d21867e189 100644 --- a/apps/sim/lib/webhooks/utils.server.ts +++ b/apps/sim/lib/webhooks/utils.server.ts @@ -133,7 +133,7 @@ async function formatTeamsGraphNotification( input: 'Teams notification received', webhook: { data: { - provider: 'microsoft-teams', + provider: 'microsoftteams', path: foundWebhook?.path || '', providerConfig: foundWebhook?.providerConfig || {}, payload: body, @@ -397,7 +397,7 @@ async function formatTeamsGraphNotification( }, webhook: { data: { - provider: 'microsoft-teams', + provider: 'microsoftteams', path: foundWebhook?.path || '', providerConfig: foundWebhook?.providerConfig || {}, payload: body, @@ -446,7 +446,7 @@ async function formatTeamsGraphNotification( }, webhook: { data: { - provider: 'microsoft-teams', + provider: 'microsoftteams', path: foundWebhook?.path || '', providerConfig: foundWebhook?.providerConfig || {}, payload: body, @@ -818,7 +818,7 @@ export async function formatWebhookInput( } } - if (foundWebhook.provider === 'microsoft-teams') { + if (foundWebhook.provider === 'microsoftteams') { // Check if this is a Microsoft Graph change notification if (body?.value && Array.isArray(body.value) && body.value.length > 0) { return await formatTeamsGraphNotification(body, foundWebhook, foundWorkflow, request) @@ -875,7 +875,7 @@ export async function formatWebhookInput( webhook: { data: { - provider: 'microsoft-teams', + provider: 'microsoftteams', path: foundWebhook.path, providerConfig: foundWebhook.providerConfig, payload: body, @@ -1653,7 +1653,7 @@ export function verifyProviderWebhook( break } - case 'microsoft-teams': + case 'microsoftteams': break case 'generic': if (providerConfig.requireAuth) { diff --git a/apps/sim/lib/webhooks/webhook-helpers.ts b/apps/sim/lib/webhooks/webhook-helpers.ts index 9d9d6775a1..c1ccf976b7 100644 --- a/apps/sim/lib/webhooks/webhook-helpers.ts +++ b/apps/sim/lib/webhooks/webhook-helpers.ts @@ -623,7 +623,7 @@ export async function cleanupExternalWebhook( ): Promise { if (webhook.provider === 'airtable') { await deleteAirtableWebhook(webhook, workflow, requestId) - } else if (webhook.provider === 'microsoft-teams') { + } else if (webhook.provider === 'microsoftteams') { await deleteTeamsSubscription(webhook, workflow, requestId) } else if (webhook.provider === 'telegram') { await deleteTelegramWebhook(webhook, requestId) diff --git a/apps/sim/triggers/microsoftteams/chat_webhook.ts b/apps/sim/triggers/microsoftteams/chat_webhook.ts index 563e033509..b318742680 100644 --- a/apps/sim/triggers/microsoftteams/chat_webhook.ts +++ b/apps/sim/triggers/microsoftteams/chat_webhook.ts @@ -4,7 +4,7 @@ import type { TriggerConfig } from '@/triggers/types' export const microsoftTeamsChatSubscriptionTrigger: TriggerConfig = { id: 'microsoftteams_chat_subscription', name: 'Microsoft Teams Chat', - provider: 'microsoft-teams', + provider: 'microsoftteams', description: 'Trigger workflow from new messages in Microsoft Teams chats via Microsoft Graph subscriptions', version: '1.0.0', @@ -16,7 +16,7 @@ export const microsoftTeamsChatSubscriptionTrigger: TriggerConfig = { title: 'Credentials', type: 'oauth-input', description: 'This trigger requires microsoft teams credentials to access your account.', - provider: 'microsoft-teams', + provider: 'microsoftteams', requiredScopes: [ 'openid', 'profile', diff --git a/apps/sim/triggers/microsoftteams/webhook.ts b/apps/sim/triggers/microsoftteams/webhook.ts index 1777c5d307..b21a407dcc 100644 --- a/apps/sim/triggers/microsoftteams/webhook.ts +++ b/apps/sim/triggers/microsoftteams/webhook.ts @@ -4,7 +4,7 @@ import type { TriggerConfig } from '../types' export const microsoftTeamsWebhookTrigger: TriggerConfig = { id: 'microsoftteams_webhook', name: 'Microsoft Teams Channel', - provider: 'microsoft-teams', + provider: 'microsoftteams', description: 'Trigger workflow from Microsoft Teams channel messages via outgoing webhooks', version: '1.0.0', icon: MicrosoftTeamsIcon, From 5ad319dcc654e5842b6d43bc7ee32a30d285fda4 Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Mon, 17 Nov 2025 16:11:20 -0800 Subject: [PATCH 4/8] Revert "change to microsoftteams" This reverts commit 217f808641acf16cd64eae2ba7a2d6aee72f500b. --- apps/sim/app/api/cron/renew-subscriptions/route.ts | 2 +- apps/sim/app/api/webhooks/route.ts | 4 ++-- apps/sim/app/api/webhooks/test/route.ts | 2 +- .../sim/executor/handlers/trigger/trigger-handler.ts | 2 +- apps/sim/lib/webhooks/processor.ts | 12 ++++++------ apps/sim/lib/webhooks/provider-utils.ts | 2 +- apps/sim/lib/webhooks/utils.server.ts | 12 ++++++------ apps/sim/lib/webhooks/webhook-helpers.ts | 2 +- apps/sim/triggers/microsoftteams/chat_webhook.ts | 4 ++-- apps/sim/triggers/microsoftteams/webhook.ts | 2 +- 10 files changed, 22 insertions(+), 22 deletions(-) diff --git a/apps/sim/app/api/cron/renew-subscriptions/route.ts b/apps/sim/app/api/cron/renew-subscriptions/route.ts index 2f58f5faa3..214399345a 100644 --- a/apps/sim/app/api/cron/renew-subscriptions/route.ts +++ b/apps/sim/app/api/cron/renew-subscriptions/route.ts @@ -35,7 +35,7 @@ export async function GET(request: NextRequest) { }) .from(webhookTable) .innerJoin(workflowTable, eq(webhookTable.workflowId, workflowTable.id)) - .where(and(eq(webhookTable.isActive, true), eq(webhookTable.provider, 'microsoftteams'))) + .where(and(eq(webhookTable.isActive, true), eq(webhookTable.provider, 'microsoft-teams'))) logger.info( `Found ${webhooksWithWorkflows.length} active Teams webhooks, checking for expiring subscriptions` diff --git a/apps/sim/app/api/webhooks/route.ts b/apps/sim/app/api/webhooks/route.ts index 86fd3ff644..5f0d58a27e 100644 --- a/apps/sim/app/api/webhooks/route.ts +++ b/apps/sim/app/api/webhooks/route.ts @@ -137,7 +137,7 @@ export async function POST(request: NextRequest) { const isCredentialBased = credentialBasedProviders.includes(provider) // Treat Microsoft Teams chat subscription as credential-based for path generation purposes const isMicrosoftTeamsChatSubscription = - provider === 'microsoftteams' && + provider === 'microsoft-teams' && typeof providerConfig === 'object' && providerConfig?.triggerId === 'microsoftteams_chat_subscription' @@ -297,7 +297,7 @@ export async function POST(request: NextRequest) { } } - if (provider === 'microsoftteams') { + if (provider === 'microsoft-teams') { const { createTeamsSubscription } = await import('@/lib/webhooks/webhook-helpers') logger.info(`[${requestId}] Creating Teams subscription before saving to database`) try { diff --git a/apps/sim/app/api/webhooks/test/route.ts b/apps/sim/app/api/webhooks/test/route.ts index 8467f53063..3de2e19b1b 100644 --- a/apps/sim/app/api/webhooks/test/route.ts +++ b/apps/sim/app/api/webhooks/test/route.ts @@ -441,7 +441,7 @@ export async function GET(request: NextRequest) { }) } - case 'microsoftteams': { + case 'microsoft-teams': { const hmacSecret = providerConfig.hmacSecret if (!hmacSecret) { diff --git a/apps/sim/executor/handlers/trigger/trigger-handler.ts b/apps/sim/executor/handlers/trigger/trigger-handler.ts index 85fbffb4ab..1b53cd8641 100644 --- a/apps/sim/executor/handlers/trigger/trigger-handler.ts +++ b/apps/sim/executor/handlers/trigger/trigger-handler.ts @@ -55,7 +55,7 @@ export class TriggerBlockHandler implements BlockHandler { } } - if (provider === 'microsoftteams') { + if (provider === 'microsoft-teams') { const providerData = (starterOutput as any)[provider] || webhookData[provider] || {} const payloadSource = providerData?.message?.raw || webhookData.payload || {} return { diff --git a/apps/sim/lib/webhooks/processor.ts b/apps/sim/lib/webhooks/processor.ts index 1b76610096..5d58b36d06 100644 --- a/apps/sim/lib/webhooks/processor.ts +++ b/apps/sim/lib/webhooks/processor.ts @@ -250,7 +250,7 @@ export async function verifyProviderAuth( const rawProviderConfig = (foundWebhook.providerConfig as Record) || {} const providerConfig = resolveProviderConfigEnvVars(rawProviderConfig, decryptedEnvVars) - if (foundWebhook.provider === 'microsoftteams') { + if (foundWebhook.provider === 'microsoft-teams') { if (providerConfig.hmacSecret) { const authHeader = request.headers.get('authorization') @@ -556,7 +556,7 @@ export async function checkRateLimits( traceSpans: [], }) - if (foundWebhook.provider === 'microsoftteams') { + if (foundWebhook.provider === 'microsoft-teams') { return NextResponse.json( { type: 'message', @@ -634,7 +634,7 @@ export async function checkUsageLimits( traceSpans: [], }) - if (foundWebhook.provider === 'microsoftteams') { + if (foundWebhook.provider === 'microsoft-teams') { return NextResponse.json( { type: 'message', @@ -783,7 +783,7 @@ export async function queueWebhookExecution( // For Microsoft Teams Graph notifications, extract unique identifiers for idempotency if ( - foundWebhook.provider === 'microsoftteams' && + foundWebhook.provider === 'microsoft-teams' && body?.value && Array.isArray(body.value) && body.value.length > 0 @@ -835,7 +835,7 @@ export async function queueWebhookExecution( ) } - if (foundWebhook.provider === 'microsoftteams') { + if (foundWebhook.provider === 'microsoft-teams') { const providerConfig = (foundWebhook.providerConfig as Record) || {} const triggerId = providerConfig.triggerId as string | undefined @@ -886,7 +886,7 @@ export async function queueWebhookExecution( } catch (error: any) { logger.error(`[${options.requestId}] Failed to queue webhook execution:`, error) - if (foundWebhook.provider === 'microsoftteams') { + if (foundWebhook.provider === 'microsoft-teams') { return NextResponse.json( { type: 'message', diff --git a/apps/sim/lib/webhooks/provider-utils.ts b/apps/sim/lib/webhooks/provider-utils.ts index 4c94335cb9..2ae0681e94 100644 --- a/apps/sim/lib/webhooks/provider-utils.ts +++ b/apps/sim/lib/webhooks/provider-utils.ts @@ -71,7 +71,7 @@ const PROVIDER_EXTRACTORS: Record string | null> = { hubspot: extractHubSpotIdentifier, linear: extractLinearIdentifier, jira: extractJiraIdentifier, - microsoftteams: extractMicrosoftTeamsIdentifier, + 'microsoft-teams': extractMicrosoftTeamsIdentifier, airtable: extractAirtableIdentifier, } diff --git a/apps/sim/lib/webhooks/utils.server.ts b/apps/sim/lib/webhooks/utils.server.ts index d21867e189..47aacdba32 100644 --- a/apps/sim/lib/webhooks/utils.server.ts +++ b/apps/sim/lib/webhooks/utils.server.ts @@ -133,7 +133,7 @@ async function formatTeamsGraphNotification( input: 'Teams notification received', webhook: { data: { - provider: 'microsoftteams', + provider: 'microsoft-teams', path: foundWebhook?.path || '', providerConfig: foundWebhook?.providerConfig || {}, payload: body, @@ -397,7 +397,7 @@ async function formatTeamsGraphNotification( }, webhook: { data: { - provider: 'microsoftteams', + provider: 'microsoft-teams', path: foundWebhook?.path || '', providerConfig: foundWebhook?.providerConfig || {}, payload: body, @@ -446,7 +446,7 @@ async function formatTeamsGraphNotification( }, webhook: { data: { - provider: 'microsoftteams', + provider: 'microsoft-teams', path: foundWebhook?.path || '', providerConfig: foundWebhook?.providerConfig || {}, payload: body, @@ -818,7 +818,7 @@ export async function formatWebhookInput( } } - if (foundWebhook.provider === 'microsoftteams') { + if (foundWebhook.provider === 'microsoft-teams') { // Check if this is a Microsoft Graph change notification if (body?.value && Array.isArray(body.value) && body.value.length > 0) { return await formatTeamsGraphNotification(body, foundWebhook, foundWorkflow, request) @@ -875,7 +875,7 @@ export async function formatWebhookInput( webhook: { data: { - provider: 'microsoftteams', + provider: 'microsoft-teams', path: foundWebhook.path, providerConfig: foundWebhook.providerConfig, payload: body, @@ -1653,7 +1653,7 @@ export function verifyProviderWebhook( break } - case 'microsoftteams': + case 'microsoft-teams': break case 'generic': if (providerConfig.requireAuth) { diff --git a/apps/sim/lib/webhooks/webhook-helpers.ts b/apps/sim/lib/webhooks/webhook-helpers.ts index c1ccf976b7..9d9d6775a1 100644 --- a/apps/sim/lib/webhooks/webhook-helpers.ts +++ b/apps/sim/lib/webhooks/webhook-helpers.ts @@ -623,7 +623,7 @@ export async function cleanupExternalWebhook( ): Promise { if (webhook.provider === 'airtable') { await deleteAirtableWebhook(webhook, workflow, requestId) - } else if (webhook.provider === 'microsoftteams') { + } else if (webhook.provider === 'microsoft-teams') { await deleteTeamsSubscription(webhook, workflow, requestId) } else if (webhook.provider === 'telegram') { await deleteTelegramWebhook(webhook, requestId) diff --git a/apps/sim/triggers/microsoftteams/chat_webhook.ts b/apps/sim/triggers/microsoftteams/chat_webhook.ts index b318742680..563e033509 100644 --- a/apps/sim/triggers/microsoftteams/chat_webhook.ts +++ b/apps/sim/triggers/microsoftteams/chat_webhook.ts @@ -4,7 +4,7 @@ import type { TriggerConfig } from '@/triggers/types' export const microsoftTeamsChatSubscriptionTrigger: TriggerConfig = { id: 'microsoftteams_chat_subscription', name: 'Microsoft Teams Chat', - provider: 'microsoftteams', + provider: 'microsoft-teams', description: 'Trigger workflow from new messages in Microsoft Teams chats via Microsoft Graph subscriptions', version: '1.0.0', @@ -16,7 +16,7 @@ export const microsoftTeamsChatSubscriptionTrigger: TriggerConfig = { title: 'Credentials', type: 'oauth-input', description: 'This trigger requires microsoft teams credentials to access your account.', - provider: 'microsoftteams', + provider: 'microsoft-teams', requiredScopes: [ 'openid', 'profile', diff --git a/apps/sim/triggers/microsoftteams/webhook.ts b/apps/sim/triggers/microsoftteams/webhook.ts index b21a407dcc..1777c5d307 100644 --- a/apps/sim/triggers/microsoftteams/webhook.ts +++ b/apps/sim/triggers/microsoftteams/webhook.ts @@ -4,7 +4,7 @@ import type { TriggerConfig } from '../types' export const microsoftTeamsWebhookTrigger: TriggerConfig = { id: 'microsoftteams_webhook', name: 'Microsoft Teams Channel', - provider: 'microsoftteams', + provider: 'microsoft-teams', description: 'Trigger workflow from Microsoft Teams channel messages via outgoing webhooks', version: '1.0.0', icon: MicrosoftTeamsIcon, From dbb43a6fac29655cdc1b842f9ef5b45ac51708ac Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Mon, 17 Nov 2025 16:12:28 -0800 Subject: [PATCH 5/8] fix --- apps/sim/app/api/cron/renew-subscriptions/route.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/sim/app/api/cron/renew-subscriptions/route.ts b/apps/sim/app/api/cron/renew-subscriptions/route.ts index 214399345a..501fdfdc43 100644 --- a/apps/sim/app/api/cron/renew-subscriptions/route.ts +++ b/apps/sim/app/api/cron/renew-subscriptions/route.ts @@ -1,6 +1,6 @@ import { db } from '@sim/db' import { webhook as webhookTable, workflow as workflowTable } from '@sim/db/schema' -import { and, eq } from 'drizzle-orm' +import { and, eq, or } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { verifyCronAuth } from '@/lib/auth/internal' import { createLogger } from '@/lib/logs/console/logger' @@ -35,7 +35,15 @@ export async function GET(request: NextRequest) { }) .from(webhookTable) .innerJoin(workflowTable, eq(webhookTable.workflowId, workflowTable.id)) - .where(and(eq(webhookTable.isActive, true), eq(webhookTable.provider, 'microsoft-teams'))) + .where( + and( + eq(webhookTable.isActive, true), + or( + eq(webhookTable.provider, 'microsoft-teams'), + eq(webhookTable.provider, 'microsoftteams') + ) + ) + ) logger.info( `Found ${webhooksWithWorkflows.length} active Teams webhooks, checking for expiring subscriptions` From e978f26221d42512b9176a7b3f13702a98b7ece5 Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Mon, 17 Nov 2025 16:21:37 -0800 Subject: [PATCH 6/8] fix --- apps/sim/lib/oauth/oauth.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/sim/lib/oauth/oauth.ts b/apps/sim/lib/oauth/oauth.ts index d26cfa7f16..8369ae4ad1 100644 --- a/apps/sim/lib/oauth/oauth.ts +++ b/apps/sim/lib/oauth/oauth.ts @@ -907,6 +907,14 @@ export function parseProvider(provider: OAuthProvider): ProviderConfig { } } + // Handle microsoftteams (no hyphen) -> microsoft-teams (with hyphen) + if (provider === 'microsoftteams') { + return { + baseProvider: 'microsoft', + featureType: 'teams', + } + } + // Handle compound providers (e.g., 'google-email' -> { baseProvider: 'google', featureType: 'email' }) const [base, feature] = provider.split('-') From d0efec14dc875399d58075f31d2505ecf42cd69e Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Mon, 17 Nov 2025 16:24:07 -0800 Subject: [PATCH 7/8] fix provider name --- apps/sim/lib/oauth/oauth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/sim/lib/oauth/oauth.ts b/apps/sim/lib/oauth/oauth.ts index 8369ae4ad1..a13a3a3b26 100644 --- a/apps/sim/lib/oauth/oauth.ts +++ b/apps/sim/lib/oauth/oauth.ts @@ -911,7 +911,7 @@ export function parseProvider(provider: OAuthProvider): ProviderConfig { if (provider === 'microsoftteams') { return { baseProvider: 'microsoft', - featureType: 'teams', + featureType: 'microsoft-teams', } } From 329bb94ee8ef21d177cd8e02d6d10d5f51b9972b Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Mon, 17 Nov 2025 17:39:36 -0800 Subject: [PATCH 8/8] fix oauth for msteams --- apps/sim/lib/oauth/oauth.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/apps/sim/lib/oauth/oauth.ts b/apps/sim/lib/oauth/oauth.ts index a13a3a3b26..ff131effc8 100644 --- a/apps/sim/lib/oauth/oauth.ts +++ b/apps/sim/lib/oauth/oauth.ts @@ -906,14 +906,24 @@ export function parseProvider(provider: OAuthProvider): ProviderConfig { featureType: 'sharepoint', } } - - // Handle microsoftteams (no hyphen) -> microsoft-teams (with hyphen) - if (provider === 'microsoftteams') { + if (provider === 'microsoft-teams' || provider === 'microsoftteams') { return { baseProvider: 'microsoft', featureType: 'microsoft-teams', } } + if (provider === 'microsoft-excel') { + return { + baseProvider: 'microsoft', + featureType: 'microsoft-excel', + } + } + if (provider === 'microsoft-planner') { + return { + baseProvider: 'microsoft', + featureType: 'microsoft-planner', + } + } // Handle compound providers (e.g., 'google-email' -> { baseProvider: 'google', featureType: 'email' }) const [base, feature] = provider.split('-')