From 5cc5414472b5271778502207392cbea52b90d8d4 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Mon, 6 Apr 2026 11:28:00 -0700 Subject: [PATCH 1/5] feat(triggers): add Linear v2 triggers with automatic webhook registration --- apps/sim/blocks/blocks/linear.ts | 62 +++++++- apps/sim/blocks/registry.ts | 3 +- apps/sim/lib/webhooks/providers/linear.ts | 149 ++++++++++++++++++ .../sim/triggers/linear/comment_created_v2.ts | 30 ++++ .../sim/triggers/linear/comment_updated_v2.ts | 30 ++++ .../linear/customer_request_created_v2.ts | 30 ++++ .../linear/customer_request_updated_v2.ts | 30 ++++ apps/sim/triggers/linear/cycle_created_v2.ts | 30 ++++ apps/sim/triggers/linear/cycle_updated_v2.ts | 30 ++++ apps/sim/triggers/linear/index.ts | 15 ++ apps/sim/triggers/linear/issue_created_v2.ts | 37 +++++ apps/sim/triggers/linear/issue_removed_v2.ts | 30 ++++ apps/sim/triggers/linear/issue_updated_v2.ts | 30 ++++ apps/sim/triggers/linear/label_created_v2.ts | 30 ++++ apps/sim/triggers/linear/label_updated_v2.ts | 30 ++++ .../sim/triggers/linear/project_created_v2.ts | 30 ++++ .../linear/project_update_created_v2.ts | 30 ++++ .../sim/triggers/linear/project_updated_v2.ts | 30 ++++ apps/sim/triggers/linear/utils.ts | 141 ++++++++++++++++- apps/sim/triggers/linear/webhook_v2.ts | 66 ++++++++ apps/sim/triggers/registry.ts | 30 ++++ 21 files changed, 889 insertions(+), 4 deletions(-) create mode 100644 apps/sim/triggers/linear/comment_created_v2.ts create mode 100644 apps/sim/triggers/linear/comment_updated_v2.ts create mode 100644 apps/sim/triggers/linear/customer_request_created_v2.ts create mode 100644 apps/sim/triggers/linear/customer_request_updated_v2.ts create mode 100644 apps/sim/triggers/linear/cycle_created_v2.ts create mode 100644 apps/sim/triggers/linear/cycle_updated_v2.ts create mode 100644 apps/sim/triggers/linear/issue_created_v2.ts create mode 100644 apps/sim/triggers/linear/issue_removed_v2.ts create mode 100644 apps/sim/triggers/linear/issue_updated_v2.ts create mode 100644 apps/sim/triggers/linear/label_created_v2.ts create mode 100644 apps/sim/triggers/linear/label_updated_v2.ts create mode 100644 apps/sim/triggers/linear/project_created_v2.ts create mode 100644 apps/sim/triggers/linear/project_update_created_v2.ts create mode 100644 apps/sim/triggers/linear/project_updated_v2.ts create mode 100644 apps/sim/triggers/linear/webhook_v2.ts diff --git a/apps/sim/blocks/blocks/linear.ts b/apps/sim/blocks/blocks/linear.ts index 18dad4d8574..6b808a19a4a 100644 --- a/apps/sim/blocks/blocks/linear.ts +++ b/apps/sim/blocks/blocks/linear.ts @@ -8,8 +8,9 @@ import { getTrigger } from '@/triggers' export const LinearBlock: BlockConfig = { type: 'linear', - name: 'Linear', + name: 'Linear (Legacy)', description: 'Interact with Linear issues, projects, and more', + hideFromToolbar: true, authMode: AuthMode.OAuth, triggerAllowed: true, longDescription: @@ -2543,3 +2544,62 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n ], }, } + +/** + * Linear V2 Block + * + * Uses automatic webhook registration via the Linear GraphQL API. + * Inherits all tool operations from the legacy block. + */ +export const LinearV2Block: BlockConfig = { + ...LinearBlock, + type: 'linear_v2', + name: 'Linear', + hideFromToolbar: false, + subBlocks: [ + ...LinearBlock.subBlocks.filter( + (sb) => + !sb.id?.startsWith('webhookUrlDisplay') && + !sb.id?.startsWith('webhookSecret') && + !sb.id?.startsWith('triggerSave') && + !sb.id?.startsWith('triggerInstructions') && + !sb.id?.startsWith('selectedTriggerId') + ), + // V2 Trigger SubBlocks + ...getTrigger('linear_issue_created_v2').subBlocks, + ...getTrigger('linear_issue_updated_v2').subBlocks, + ...getTrigger('linear_issue_removed_v2').subBlocks, + ...getTrigger('linear_comment_created_v2').subBlocks, + ...getTrigger('linear_comment_updated_v2').subBlocks, + ...getTrigger('linear_project_created_v2').subBlocks, + ...getTrigger('linear_project_updated_v2').subBlocks, + ...getTrigger('linear_cycle_created_v2').subBlocks, + ...getTrigger('linear_cycle_updated_v2').subBlocks, + ...getTrigger('linear_label_created_v2').subBlocks, + ...getTrigger('linear_label_updated_v2').subBlocks, + ...getTrigger('linear_project_update_created_v2').subBlocks, + ...getTrigger('linear_customer_request_created_v2').subBlocks, + ...getTrigger('linear_customer_request_updated_v2').subBlocks, + ...getTrigger('linear_webhook_v2').subBlocks, + ], + triggers: { + enabled: true, + available: [ + 'linear_issue_created_v2', + 'linear_issue_updated_v2', + 'linear_issue_removed_v2', + 'linear_comment_created_v2', + 'linear_comment_updated_v2', + 'linear_project_created_v2', + 'linear_project_updated_v2', + 'linear_cycle_created_v2', + 'linear_cycle_updated_v2', + 'linear_label_created_v2', + 'linear_label_updated_v2', + 'linear_project_update_created_v2', + 'linear_customer_request_created_v2', + 'linear_customer_request_updated_v2', + 'linear_webhook_v2', + ], + }, +} diff --git a/apps/sim/blocks/registry.ts b/apps/sim/blocks/registry.ts index 8b21cebea1a..dd9ea3f72e5 100644 --- a/apps/sim/blocks/registry.ts +++ b/apps/sim/blocks/registry.ts @@ -102,7 +102,7 @@ import { KnowledgeBlock } from '@/blocks/blocks/knowledge' import { LangsmithBlock } from '@/blocks/blocks/langsmith' import { LaunchDarklyBlock } from '@/blocks/blocks/launchdarkly' import { LemlistBlock } from '@/blocks/blocks/lemlist' -import { LinearBlock } from '@/blocks/blocks/linear' +import { LinearBlock, LinearV2Block } from '@/blocks/blocks/linear' import { LinkedInBlock } from '@/blocks/blocks/linkedin' import { LinkupBlock } from '@/blocks/blocks/linkup' import { LoopsBlock } from '@/blocks/blocks/loops' @@ -337,6 +337,7 @@ export const registry: Record = { launchdarkly: LaunchDarklyBlock, lemlist: LemlistBlock, linear: LinearBlock, + linear_v2: LinearV2Block, linkedin: LinkedInBlock, linkup: LinkupBlock, loops: LoopsBlock, diff --git a/apps/sim/lib/webhooks/providers/linear.ts b/apps/sim/lib/webhooks/providers/linear.ts index 9372b8d6009..2e0889bb75e 100644 --- a/apps/sim/lib/webhooks/providers/linear.ts +++ b/apps/sim/lib/webhooks/providers/linear.ts @@ -1,9 +1,15 @@ import crypto from 'crypto' import { createLogger } from '@sim/logger' import { safeCompare } from '@/lib/core/security/encryption' +import { generateId } from '@/lib/core/utils/uuid' +import { getNotificationUrl, getProviderConfig } from '@/lib/webhooks/providers/subscription-utils' import type { + DeleteSubscriptionContext, + EventMatchContext, FormatInputContext, FormatInputResult, + SubscriptionContext, + SubscriptionResult, WebhookProviderHandler, } from '@/lib/webhooks/providers/types' import { createHmacVerifier } from '@/lib/webhooks/providers/utils' @@ -60,6 +66,149 @@ export const linearHandler: WebhookProviderHandler = { } }, + async matchEvent({ body, requestId, providerConfig }: EventMatchContext) { + const triggerId = providerConfig.triggerId as string | undefined + if (triggerId && !triggerId.endsWith('_webhook') && !triggerId.endsWith('_webhook_v2')) { + const { isLinearEventMatch } = await import('@/triggers/linear/utils') + const obj = body as Record + const action = obj.action as string | undefined + const type = obj.type as string | undefined + if (!isLinearEventMatch(triggerId, type || '', action)) { + logger.debug( + `[${requestId}] Linear event mismatch for trigger ${triggerId}. Type: ${type}, Action: ${action}. Skipping.` + ) + return false + } + } + return true + }, + + async createSubscription(ctx: SubscriptionContext): Promise { + const config = getProviderConfig(ctx.webhook) + const triggerId = config.triggerId as string | undefined + + if (!triggerId || !triggerId.endsWith('_v2')) { + return undefined + } + + const apiKey = config.apiKey as string | undefined + if (!apiKey) { + logger.warn(`[${ctx.requestId}] Missing API key for Linear webhook ${ctx.webhook.id}`) + throw new Error( + 'Linear API key is required. Please provide a valid API key in the trigger configuration.' + ) + } + + const { LINEAR_RESOURCE_TYPE_MAP } = await import('@/triggers/linear/utils') + const resourceTypes = LINEAR_RESOURCE_TYPE_MAP[triggerId] + if (!resourceTypes) { + logger.warn(`[${ctx.requestId}] Unknown Linear trigger ID: ${triggerId}`) + throw new Error(`Unknown Linear trigger type: ${triggerId}`) + } + + const notificationUrl = getNotificationUrl(ctx.webhook) + const webhookSecret = generateId() + + try { + const response = await fetch('https://api.linear.app/graphql', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: apiKey, + }, + body: JSON.stringify({ + query: `mutation WebhookCreate($input: WebhookCreateInput!) { + webhookCreate(input: $input) { + success + webhook { id enabled } + } + }`, + variables: { + input: { + url: notificationUrl, + resourceTypes, + secret: webhookSecret, + enabled: true, + }, + }, + }), + }) + + const data = await response.json() + const result = data?.data?.webhookCreate + + if (!result?.success) { + const errors = data?.errors?.map((e: { message: string }) => e.message).join(', ') + logger.error(`[${ctx.requestId}] Failed to create Linear webhook`, { + errors, + webhookId: ctx.webhook.id, + }) + throw new Error(errors || 'Failed to create Linear webhook. Please verify your API key.') + } + + const externalId = result.webhook?.id + logger.info( + `[${ctx.requestId}] Created Linear webhook ${externalId} for webhook ${ctx.webhook.id}` + ) + + return { + providerConfigUpdates: { + externalId, + webhookSecret, + }, + } + } catch (error) { + if (error instanceof Error && error.message.includes('API key')) { + throw error + } + logger.error(`[${ctx.requestId}] Error creating Linear webhook`, { + error: error instanceof Error ? error.message : String(error), + }) + throw new Error('Failed to create Linear webhook. Please verify your API key and try again.') + } + }, + + async deleteSubscription(ctx: DeleteSubscriptionContext): Promise { + const config = getProviderConfig(ctx.webhook) + const externalId = config.externalId as string | undefined + const apiKey = config.apiKey as string | undefined + + if (!externalId || !apiKey) { + return + } + + try { + const response = await fetch('https://api.linear.app/graphql', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: apiKey, + }, + body: JSON.stringify({ + query: `mutation WebhookDelete($id: String!) { + webhookDelete(id: $id) { success } + }`, + variables: { id: externalId }, + }), + }) + + const data = await response.json() + if (data?.data?.webhookDelete?.success) { + logger.info( + `[${ctx.requestId}] Deleted Linear webhook ${externalId} for webhook ${ctx.webhook.id}` + ) + } else { + logger.warn( + `[${ctx.requestId}] Linear webhook deletion returned unsuccessful for ${externalId}` + ) + } + } catch (error) { + logger.warn(`[${ctx.requestId}] Error deleting Linear webhook ${externalId} (non-fatal)`, { + error: error instanceof Error ? error.message : String(error), + }) + } + }, + extractIdempotencyId(body: unknown) { const obj = body as Record const data = obj.data as Record | undefined diff --git a/apps/sim/triggers/linear/comment_created_v2.ts b/apps/sim/triggers/linear/comment_created_v2.ts new file mode 100644 index 00000000000..4db96a236f3 --- /dev/null +++ b/apps/sim/triggers/linear/comment_created_v2.ts @@ -0,0 +1,30 @@ +import { LinearIcon } from '@/components/icons' +import { buildCommentOutputs, buildLinearV2SubBlocks } from '@/triggers/linear/utils' +import type { TriggerConfig } from '@/triggers/types' + +export const linearCommentCreatedV2Trigger: TriggerConfig = { + id: 'linear_comment_created_v2', + name: 'Linear Comment Created', + provider: 'linear', + description: 'Trigger workflow when a new comment is created in Linear', + version: '2.0.0', + icon: LinearIcon, + + subBlocks: buildLinearV2SubBlocks({ + triggerId: 'linear_comment_created_v2', + eventType: 'Comment (create)', + }), + + outputs: buildCommentOutputs(), + + webhook: { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Linear-Event': 'Comment', + 'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', + 'Linear-Signature': 'sha256...', + 'User-Agent': 'Linear-Webhook', + }, + }, +} diff --git a/apps/sim/triggers/linear/comment_updated_v2.ts b/apps/sim/triggers/linear/comment_updated_v2.ts new file mode 100644 index 00000000000..6496534933a --- /dev/null +++ b/apps/sim/triggers/linear/comment_updated_v2.ts @@ -0,0 +1,30 @@ +import { LinearIcon } from '@/components/icons' +import { buildCommentOutputs, buildLinearV2SubBlocks } from '@/triggers/linear/utils' +import type { TriggerConfig } from '@/triggers/types' + +export const linearCommentUpdatedV2Trigger: TriggerConfig = { + id: 'linear_comment_updated_v2', + name: 'Linear Comment Updated', + provider: 'linear', + description: 'Trigger workflow when a comment is updated in Linear', + version: '2.0.0', + icon: LinearIcon, + + subBlocks: buildLinearV2SubBlocks({ + triggerId: 'linear_comment_updated_v2', + eventType: 'Comment (update)', + }), + + outputs: buildCommentOutputs(), + + webhook: { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Linear-Event': 'Comment', + 'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', + 'Linear-Signature': 'sha256...', + 'User-Agent': 'Linear-Webhook', + }, + }, +} diff --git a/apps/sim/triggers/linear/customer_request_created_v2.ts b/apps/sim/triggers/linear/customer_request_created_v2.ts new file mode 100644 index 00000000000..dd5d91663b7 --- /dev/null +++ b/apps/sim/triggers/linear/customer_request_created_v2.ts @@ -0,0 +1,30 @@ +import { LinearIcon } from '@/components/icons' +import { buildCustomerRequestOutputs, buildLinearV2SubBlocks } from '@/triggers/linear/utils' +import type { TriggerConfig } from '@/triggers/types' + +export const linearCustomerRequestCreatedV2Trigger: TriggerConfig = { + id: 'linear_customer_request_created_v2', + name: 'Linear Customer Request Created', + provider: 'linear', + description: 'Trigger workflow when a new customer request is created in Linear', + version: '2.0.0', + icon: LinearIcon, + + subBlocks: buildLinearV2SubBlocks({ + triggerId: 'linear_customer_request_created_v2', + eventType: 'Customer Requests', + }), + + outputs: buildCustomerRequestOutputs(), + + webhook: { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Linear-Event': 'CustomerNeed', + 'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', + 'Linear-Signature': 'sha256...', + 'User-Agent': 'Linear-Webhook', + }, + }, +} diff --git a/apps/sim/triggers/linear/customer_request_updated_v2.ts b/apps/sim/triggers/linear/customer_request_updated_v2.ts new file mode 100644 index 00000000000..b06696b580f --- /dev/null +++ b/apps/sim/triggers/linear/customer_request_updated_v2.ts @@ -0,0 +1,30 @@ +import { LinearIcon } from '@/components/icons' +import { buildCustomerRequestOutputs, buildLinearV2SubBlocks } from '@/triggers/linear/utils' +import type { TriggerConfig } from '@/triggers/types' + +export const linearCustomerRequestUpdatedV2Trigger: TriggerConfig = { + id: 'linear_customer_request_updated_v2', + name: 'Linear Customer Request Updated', + provider: 'linear', + description: 'Trigger workflow when a customer request is updated in Linear', + version: '2.0.0', + icon: LinearIcon, + + subBlocks: buildLinearV2SubBlocks({ + triggerId: 'linear_customer_request_updated_v2', + eventType: 'CustomerNeed (update)', + }), + + outputs: buildCustomerRequestOutputs(), + + webhook: { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Linear-Event': 'CustomerNeed', + 'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', + 'Linear-Signature': 'sha256...', + 'User-Agent': 'Linear-Webhook', + }, + }, +} diff --git a/apps/sim/triggers/linear/cycle_created_v2.ts b/apps/sim/triggers/linear/cycle_created_v2.ts new file mode 100644 index 00000000000..8204331c22e --- /dev/null +++ b/apps/sim/triggers/linear/cycle_created_v2.ts @@ -0,0 +1,30 @@ +import { LinearIcon } from '@/components/icons' +import { buildCycleOutputs, buildLinearV2SubBlocks } from '@/triggers/linear/utils' +import type { TriggerConfig } from '@/triggers/types' + +export const linearCycleCreatedV2Trigger: TriggerConfig = { + id: 'linear_cycle_created_v2', + name: 'Linear Cycle Created', + provider: 'linear', + description: 'Trigger workflow when a new cycle is created in Linear', + version: '2.0.0', + icon: LinearIcon, + + subBlocks: buildLinearV2SubBlocks({ + triggerId: 'linear_cycle_created_v2', + eventType: 'Cycle (create)', + }), + + outputs: buildCycleOutputs(), + + webhook: { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Linear-Event': 'Cycle', + 'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', + 'Linear-Signature': 'sha256...', + 'User-Agent': 'Linear-Webhook', + }, + }, +} diff --git a/apps/sim/triggers/linear/cycle_updated_v2.ts b/apps/sim/triggers/linear/cycle_updated_v2.ts new file mode 100644 index 00000000000..341f1261236 --- /dev/null +++ b/apps/sim/triggers/linear/cycle_updated_v2.ts @@ -0,0 +1,30 @@ +import { LinearIcon } from '@/components/icons' +import { buildCycleOutputs, buildLinearV2SubBlocks } from '@/triggers/linear/utils' +import type { TriggerConfig } from '@/triggers/types' + +export const linearCycleUpdatedV2Trigger: TriggerConfig = { + id: 'linear_cycle_updated_v2', + name: 'Linear Cycle Updated', + provider: 'linear', + description: 'Trigger workflow when a cycle is updated in Linear', + version: '2.0.0', + icon: LinearIcon, + + subBlocks: buildLinearV2SubBlocks({ + triggerId: 'linear_cycle_updated_v2', + eventType: 'Cycle (update)', + }), + + outputs: buildCycleOutputs(), + + webhook: { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Linear-Event': 'Cycle', + 'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', + 'Linear-Signature': 'sha256...', + 'User-Agent': 'Linear-Webhook', + }, + }, +} diff --git a/apps/sim/triggers/linear/index.ts b/apps/sim/triggers/linear/index.ts index 26194979226..1a6e9ebe2a1 100644 --- a/apps/sim/triggers/linear/index.ts +++ b/apps/sim/triggers/linear/index.ts @@ -1,15 +1,30 @@ export { linearCommentCreatedTrigger } from './comment_created' +export { linearCommentCreatedV2Trigger } from './comment_created_v2' export { linearCommentUpdatedTrigger } from './comment_updated' +export { linearCommentUpdatedV2Trigger } from './comment_updated_v2' export { linearCustomerRequestCreatedTrigger } from './customer_request_created' +export { linearCustomerRequestCreatedV2Trigger } from './customer_request_created_v2' export { linearCustomerRequestUpdatedTrigger } from './customer_request_updated' +export { linearCustomerRequestUpdatedV2Trigger } from './customer_request_updated_v2' export { linearCycleCreatedTrigger } from './cycle_created' +export { linearCycleCreatedV2Trigger } from './cycle_created_v2' export { linearCycleUpdatedTrigger } from './cycle_updated' +export { linearCycleUpdatedV2Trigger } from './cycle_updated_v2' export { linearIssueCreatedTrigger } from './issue_created' +export { linearIssueCreatedV2Trigger } from './issue_created_v2' export { linearIssueRemovedTrigger } from './issue_removed' +export { linearIssueRemovedV2Trigger } from './issue_removed_v2' export { linearIssueUpdatedTrigger } from './issue_updated' +export { linearIssueUpdatedV2Trigger } from './issue_updated_v2' export { linearLabelCreatedTrigger } from './label_created' +export { linearLabelCreatedV2Trigger } from './label_created_v2' export { linearLabelUpdatedTrigger } from './label_updated' +export { linearLabelUpdatedV2Trigger } from './label_updated_v2' export { linearProjectCreatedTrigger } from './project_created' +export { linearProjectCreatedV2Trigger } from './project_created_v2' export { linearProjectUpdateCreatedTrigger } from './project_update_created' +export { linearProjectUpdateCreatedV2Trigger } from './project_update_created_v2' export { linearProjectUpdatedTrigger } from './project_updated' +export { linearProjectUpdatedV2Trigger } from './project_updated_v2' export { linearWebhookTrigger } from './webhook' +export { linearWebhookV2Trigger } from './webhook_v2' diff --git a/apps/sim/triggers/linear/issue_created_v2.ts b/apps/sim/triggers/linear/issue_created_v2.ts new file mode 100644 index 00000000000..b6eb9cc5945 --- /dev/null +++ b/apps/sim/triggers/linear/issue_created_v2.ts @@ -0,0 +1,37 @@ +import { LinearIcon } from '@/components/icons' +import { buildIssueOutputs, buildLinearV2SubBlocks } from '@/triggers/linear/utils' +import type { TriggerConfig } from '@/triggers/types' + +/** + * Linear Issue Created Trigger (v2) + * + * Primary trigger - includes the dropdown for selecting trigger type. + * Uses automatic webhook registration via the Linear GraphQL API. + */ +export const linearIssueCreatedV2Trigger: TriggerConfig = { + id: 'linear_issue_created_v2', + name: 'Linear Issue Created', + provider: 'linear', + description: 'Trigger workflow when a new issue is created in Linear', + version: '2.0.0', + icon: LinearIcon, + + subBlocks: buildLinearV2SubBlocks({ + triggerId: 'linear_issue_created_v2', + eventType: 'Issue (create)', + includeDropdown: true, + }), + + outputs: buildIssueOutputs(), + + webhook: { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Linear-Event': 'Issue', + 'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', + 'Linear-Signature': 'sha256...', + 'User-Agent': 'Linear-Webhook', + }, + }, +} diff --git a/apps/sim/triggers/linear/issue_removed_v2.ts b/apps/sim/triggers/linear/issue_removed_v2.ts new file mode 100644 index 00000000000..152e1d5504f --- /dev/null +++ b/apps/sim/triggers/linear/issue_removed_v2.ts @@ -0,0 +1,30 @@ +import { LinearIcon } from '@/components/icons' +import { buildIssueOutputs, buildLinearV2SubBlocks } from '@/triggers/linear/utils' +import type { TriggerConfig } from '@/triggers/types' + +export const linearIssueRemovedV2Trigger: TriggerConfig = { + id: 'linear_issue_removed_v2', + name: 'Linear Issue Removed', + provider: 'linear', + description: 'Trigger workflow when an issue is removed/deleted in Linear', + version: '2.0.0', + icon: LinearIcon, + + subBlocks: buildLinearV2SubBlocks({ + triggerId: 'linear_issue_removed_v2', + eventType: 'Issue (remove)', + }), + + outputs: buildIssueOutputs(), + + webhook: { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Linear-Event': 'Issue', + 'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', + 'Linear-Signature': 'sha256...', + 'User-Agent': 'Linear-Webhook', + }, + }, +} diff --git a/apps/sim/triggers/linear/issue_updated_v2.ts b/apps/sim/triggers/linear/issue_updated_v2.ts new file mode 100644 index 00000000000..6fc6b04e2b9 --- /dev/null +++ b/apps/sim/triggers/linear/issue_updated_v2.ts @@ -0,0 +1,30 @@ +import { LinearIcon } from '@/components/icons' +import { buildIssueOutputs, buildLinearV2SubBlocks } from '@/triggers/linear/utils' +import type { TriggerConfig } from '@/triggers/types' + +export const linearIssueUpdatedV2Trigger: TriggerConfig = { + id: 'linear_issue_updated_v2', + name: 'Linear Issue Updated', + provider: 'linear', + description: 'Trigger workflow when an issue is updated in Linear', + version: '2.0.0', + icon: LinearIcon, + + subBlocks: buildLinearV2SubBlocks({ + triggerId: 'linear_issue_updated_v2', + eventType: 'Issue (update)', + }), + + outputs: buildIssueOutputs(), + + webhook: { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Linear-Event': 'Issue', + 'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', + 'Linear-Signature': 'sha256...', + 'User-Agent': 'Linear-Webhook', + }, + }, +} diff --git a/apps/sim/triggers/linear/label_created_v2.ts b/apps/sim/triggers/linear/label_created_v2.ts new file mode 100644 index 00000000000..d32c1593e95 --- /dev/null +++ b/apps/sim/triggers/linear/label_created_v2.ts @@ -0,0 +1,30 @@ +import { LinearIcon } from '@/components/icons' +import { buildLabelOutputs, buildLinearV2SubBlocks } from '@/triggers/linear/utils' +import type { TriggerConfig } from '@/triggers/types' + +export const linearLabelCreatedV2Trigger: TriggerConfig = { + id: 'linear_label_created_v2', + name: 'Linear Label Created', + provider: 'linear', + description: 'Trigger workflow when a new label is created in Linear', + version: '2.0.0', + icon: LinearIcon, + + subBlocks: buildLinearV2SubBlocks({ + triggerId: 'linear_label_created_v2', + eventType: 'IssueLabel (create)', + }), + + outputs: buildLabelOutputs(), + + webhook: { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Linear-Event': 'IssueLabel', + 'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', + 'Linear-Signature': 'sha256...', + 'User-Agent': 'Linear-Webhook', + }, + }, +} diff --git a/apps/sim/triggers/linear/label_updated_v2.ts b/apps/sim/triggers/linear/label_updated_v2.ts new file mode 100644 index 00000000000..f55f61f1eff --- /dev/null +++ b/apps/sim/triggers/linear/label_updated_v2.ts @@ -0,0 +1,30 @@ +import { LinearIcon } from '@/components/icons' +import { buildLabelOutputs, buildLinearV2SubBlocks } from '@/triggers/linear/utils' +import type { TriggerConfig } from '@/triggers/types' + +export const linearLabelUpdatedV2Trigger: TriggerConfig = { + id: 'linear_label_updated_v2', + name: 'Linear Label Updated', + provider: 'linear', + description: 'Trigger workflow when a label is updated in Linear', + version: '2.0.0', + icon: LinearIcon, + + subBlocks: buildLinearV2SubBlocks({ + triggerId: 'linear_label_updated_v2', + eventType: 'IssueLabel (update)', + }), + + outputs: buildLabelOutputs(), + + webhook: { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Linear-Event': 'IssueLabel', + 'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', + 'Linear-Signature': 'sha256...', + 'User-Agent': 'Linear-Webhook', + }, + }, +} diff --git a/apps/sim/triggers/linear/project_created_v2.ts b/apps/sim/triggers/linear/project_created_v2.ts new file mode 100644 index 00000000000..392cc57d894 --- /dev/null +++ b/apps/sim/triggers/linear/project_created_v2.ts @@ -0,0 +1,30 @@ +import { LinearIcon } from '@/components/icons' +import { buildLinearV2SubBlocks, buildProjectOutputs } from '@/triggers/linear/utils' +import type { TriggerConfig } from '@/triggers/types' + +export const linearProjectCreatedV2Trigger: TriggerConfig = { + id: 'linear_project_created_v2', + name: 'Linear Project Created', + provider: 'linear', + description: 'Trigger workflow when a new project is created in Linear', + version: '2.0.0', + icon: LinearIcon, + + subBlocks: buildLinearV2SubBlocks({ + triggerId: 'linear_project_created_v2', + eventType: 'Project (create)', + }), + + outputs: buildProjectOutputs(), + + webhook: { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Linear-Event': 'Project', + 'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', + 'Linear-Signature': 'sha256...', + 'User-Agent': 'Linear-Webhook', + }, + }, +} diff --git a/apps/sim/triggers/linear/project_update_created_v2.ts b/apps/sim/triggers/linear/project_update_created_v2.ts new file mode 100644 index 00000000000..cf872edd76a --- /dev/null +++ b/apps/sim/triggers/linear/project_update_created_v2.ts @@ -0,0 +1,30 @@ +import { LinearIcon } from '@/components/icons' +import { buildLinearV2SubBlocks, buildProjectUpdateOutputs } from '@/triggers/linear/utils' +import type { TriggerConfig } from '@/triggers/types' + +export const linearProjectUpdateCreatedV2Trigger: TriggerConfig = { + id: 'linear_project_update_created_v2', + name: 'Linear Project Update Created', + provider: 'linear', + description: 'Trigger workflow when a new project update is posted in Linear', + version: '2.0.0', + icon: LinearIcon, + + subBlocks: buildLinearV2SubBlocks({ + triggerId: 'linear_project_update_created_v2', + eventType: 'ProjectUpdate (create)', + }), + + outputs: buildProjectUpdateOutputs(), + + webhook: { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Linear-Event': 'ProjectUpdate', + 'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', + 'Linear-Signature': 'sha256...', + 'User-Agent': 'Linear-Webhook', + }, + }, +} diff --git a/apps/sim/triggers/linear/project_updated_v2.ts b/apps/sim/triggers/linear/project_updated_v2.ts new file mode 100644 index 00000000000..71a83211881 --- /dev/null +++ b/apps/sim/triggers/linear/project_updated_v2.ts @@ -0,0 +1,30 @@ +import { LinearIcon } from '@/components/icons' +import { buildLinearV2SubBlocks, buildProjectOutputs } from '@/triggers/linear/utils' +import type { TriggerConfig } from '@/triggers/types' + +export const linearProjectUpdatedV2Trigger: TriggerConfig = { + id: 'linear_project_updated_v2', + name: 'Linear Project Updated', + provider: 'linear', + description: 'Trigger workflow when a project is updated in Linear', + version: '2.0.0', + icon: LinearIcon, + + subBlocks: buildLinearV2SubBlocks({ + triggerId: 'linear_project_updated_v2', + eventType: 'Project (update)', + }), + + outputs: buildProjectOutputs(), + + webhook: { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Linear-Event': 'Project', + 'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', + 'Linear-Signature': 'sha256...', + 'User-Agent': 'Linear-Webhook', + }, + }, +} diff --git a/apps/sim/triggers/linear/utils.ts b/apps/sim/triggers/linear/utils.ts index 5002b3dada3..56f60d07059 100644 --- a/apps/sim/triggers/linear/utils.ts +++ b/apps/sim/triggers/linear/utils.ts @@ -1,3 +1,4 @@ +import type { SubBlockConfig } from '@/blocks/types' import type { TriggerOutput } from '@/triggers/types' /** @@ -22,7 +23,37 @@ export const linearTriggerOptions = [ ] /** - * Generate setup instructions for a specific Linear event type + * Maps trigger IDs to Linear resource types for webhook creation. + * Used by the automatic webhook registration in provider-subscriptions. + */ +export const LINEAR_RESOURCE_TYPE_MAP: Record = { + linear_issue_created_v2: ['Issue'], + linear_issue_updated_v2: ['Issue'], + linear_issue_removed_v2: ['Issue'], + linear_comment_created_v2: ['Comment'], + linear_comment_updated_v2: ['Comment'], + linear_project_created_v2: ['Project'], + linear_project_updated_v2: ['Project'], + linear_cycle_created_v2: ['Cycle'], + linear_cycle_updated_v2: ['Cycle'], + linear_label_created_v2: ['IssueLabel'], + linear_label_updated_v2: ['IssueLabel'], + linear_project_update_created_v2: ['ProjectUpdate'], + linear_customer_request_created_v2: ['CustomerNeed'], + linear_customer_request_updated_v2: ['CustomerNeed'], + linear_webhook_v2: [ + 'Issue', + 'Comment', + 'Project', + 'Cycle', + 'IssueLabel', + 'ProjectUpdate', + 'CustomerNeed', + ], +} + +/** + * Generate setup instructions for manual Linear webhook configuration (v1 triggers) */ export function linearSetupInstructions(eventType: string, additionalNotes?: string): string { const instructions = [ @@ -47,6 +78,111 @@ export function linearSetupInstructions(eventType: string, additionalNotes?: str .join('') } +/** + * Generate setup instructions for automatic Linear webhook creation (v2 triggers) + */ +export function linearV2SetupInstructions(eventType: string, additionalNotes?: string): string { + const instructions = [ + 'Enter your Linear API Key above. You can create one in Linear at Settings > API > Personal API keys.', + `Click "Save Configuration" to automatically create the webhook in Linear for ${eventType} events.`, + 'The webhook will be automatically deleted when you remove this trigger.', + ] + + if (additionalNotes) { + instructions.push(additionalNotes) + } + + return instructions + .map( + (instruction, index) => + `
${index + 1}. ${instruction}
` + ) + .join('') +} + +/** + * V2 trigger dropdown options with _v2 suffixed IDs + */ +export const linearV2TriggerOptions = [ + { label: 'Issue Created', id: 'linear_issue_created_v2' }, + { label: 'Issue Updated', id: 'linear_issue_updated_v2' }, + { label: 'Issue Removed', id: 'linear_issue_removed_v2' }, + { label: 'Comment Created', id: 'linear_comment_created_v2' }, + { label: 'Comment Updated', id: 'linear_comment_updated_v2' }, + { label: 'Project Created', id: 'linear_project_created_v2' }, + { label: 'Project Updated', id: 'linear_project_updated_v2' }, + { label: 'Cycle Created', id: 'linear_cycle_created_v2' }, + { label: 'Cycle Updated', id: 'linear_cycle_updated_v2' }, + { label: 'Label Created', id: 'linear_label_created_v2' }, + { label: 'Label Updated', id: 'linear_label_updated_v2' }, + { label: 'Project Update Created', id: 'linear_project_update_created_v2' }, + { label: 'Customer Request Created', id: 'linear_customer_request_created_v2' }, + { label: 'Customer Request Updated', id: 'linear_customer_request_updated_v2' }, + { label: 'General Webhook (All Events)', id: 'linear_webhook_v2' }, +] + +/** + * Builds the complete subBlocks array for a v2 Linear trigger. + * Webhooks are managed via API, so no webhook URL is displayed. + * + * Structure: [dropdown?] -> apiKey -> triggerSave -> instructions + */ +export function buildLinearV2SubBlocks(options: { + triggerId: string + eventType: string + includeDropdown?: boolean + additionalNotes?: string +}): SubBlockConfig[] { + const { triggerId, eventType, includeDropdown = false, additionalNotes } = options + const blocks: SubBlockConfig[] = [] + + if (includeDropdown) { + blocks.push({ + id: 'selectedTriggerId', + title: 'Trigger Type', + type: 'dropdown', + mode: 'trigger', + options: linearV2TriggerOptions, + value: () => triggerId, + required: true, + }) + } + + blocks.push({ + id: 'apiKey', + title: 'API Key', + type: 'short-input', + placeholder: 'Enter your Linear API key', + password: true, + required: true, + paramVisibility: 'user-only', + mode: 'trigger', + condition: { field: 'selectedTriggerId', value: triggerId }, + }) + + blocks.push({ + id: 'triggerSave', + title: '', + type: 'trigger-save', + hideFromPreview: true, + mode: 'trigger', + triggerId, + condition: { field: 'selectedTriggerId', value: triggerId }, + }) + + blocks.push({ + id: 'triggerInstructions', + title: 'Setup Instructions', + hideFromPreview: true, + type: 'text', + defaultValue: linearV2SetupInstructions(eventType, additionalNotes), + mode: 'trigger', + condition: { field: 'selectedTriggerId', value: triggerId }, + }) + + return blocks +} + /** * Shared user/actor output schema * Note: Linear webhooks only include id, name, and type in actor objects @@ -900,7 +1036,8 @@ export function isLinearEventMatch(triggerId: string, eventType: string, action? linear_customer_request_updated: { type: 'CustomerNeed', actions: ['update'] }, } - const config = eventMap[triggerId] + const normalizedId = triggerId.replace(/_v2$/, '') + const config = eventMap[normalizedId] if (!config) { return true // Unknown trigger, allow through } diff --git a/apps/sim/triggers/linear/webhook_v2.ts b/apps/sim/triggers/linear/webhook_v2.ts new file mode 100644 index 00000000000..4b84363ca7b --- /dev/null +++ b/apps/sim/triggers/linear/webhook_v2.ts @@ -0,0 +1,66 @@ +import { LinearIcon } from '@/components/icons' +import { buildLinearV2SubBlocks, userOutputs } from '@/triggers/linear/utils' +import type { TriggerConfig } from '@/triggers/types' + +export const linearWebhookV2Trigger: TriggerConfig = { + id: 'linear_webhook_v2', + name: 'Linear Webhook', + provider: 'linear', + description: 'Trigger workflow from any Linear webhook event', + version: '2.0.0', + icon: LinearIcon, + + subBlocks: buildLinearV2SubBlocks({ + triggerId: 'linear_webhook_v2', + eventType: 'All Events', + additionalNotes: + 'This webhook will receive all Linear events. Use the type and action fields in the payload to filter and handle different event types.', + }), + + outputs: { + action: { + type: 'string', + description: 'Action performed (create, update, remove)', + }, + type: { + type: 'string', + description: 'Entity type (Issue, Comment, Project, Cycle, IssueLabel, ProjectUpdate, etc.)', + }, + webhookId: { + type: 'string', + description: 'Webhook ID', + }, + webhookTimestamp: { + type: 'number', + description: 'Webhook timestamp (milliseconds)', + }, + organizationId: { + type: 'string', + description: 'Organization ID', + }, + createdAt: { + type: 'string', + description: 'Event creation timestamp', + }, + actor: userOutputs, + data: { + type: 'object', + description: 'Complete entity data object', + }, + updatedFrom: { + type: 'object', + description: 'Previous values for changed fields (only present on update)', + }, + }, + + webhook: { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Linear-Event': 'Issue', + 'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', + 'Linear-Signature': 'sha256...', + 'User-Agent': 'Linear-Webhook', + }, + }, +} diff --git a/apps/sim/triggers/registry.ts b/apps/sim/triggers/registry.ts index 4390bfeefff..424a0a6abb4 100644 --- a/apps/sim/triggers/registry.ts +++ b/apps/sim/triggers/registry.ts @@ -142,20 +142,35 @@ import { } from '@/triggers/lemlist' import { linearCommentCreatedTrigger, + linearCommentCreatedV2Trigger, linearCommentUpdatedTrigger, + linearCommentUpdatedV2Trigger, linearCustomerRequestCreatedTrigger, + linearCustomerRequestCreatedV2Trigger, linearCustomerRequestUpdatedTrigger, + linearCustomerRequestUpdatedV2Trigger, linearCycleCreatedTrigger, + linearCycleCreatedV2Trigger, linearCycleUpdatedTrigger, + linearCycleUpdatedV2Trigger, linearIssueCreatedTrigger, + linearIssueCreatedV2Trigger, linearIssueRemovedTrigger, + linearIssueRemovedV2Trigger, linearIssueUpdatedTrigger, + linearIssueUpdatedV2Trigger, linearLabelCreatedTrigger, + linearLabelCreatedV2Trigger, linearLabelUpdatedTrigger, + linearLabelUpdatedV2Trigger, linearProjectCreatedTrigger, + linearProjectCreatedV2Trigger, linearProjectUpdateCreatedTrigger, + linearProjectUpdateCreatedV2Trigger, linearProjectUpdatedTrigger, + linearProjectUpdatedV2Trigger, linearWebhookTrigger, + linearWebhookV2Trigger, } from '@/triggers/linear' import { microsoftTeamsChatSubscriptionTrigger, @@ -295,6 +310,21 @@ export const TRIGGER_REGISTRY: TriggerRegistry = { linear_project_update_created: linearProjectUpdateCreatedTrigger, linear_customer_request_created: linearCustomerRequestCreatedTrigger, linear_customer_request_updated: linearCustomerRequestUpdatedTrigger, + linear_webhook_v2: linearWebhookV2Trigger, + linear_issue_created_v2: linearIssueCreatedV2Trigger, + linear_issue_updated_v2: linearIssueUpdatedV2Trigger, + linear_issue_removed_v2: linearIssueRemovedV2Trigger, + linear_comment_created_v2: linearCommentCreatedV2Trigger, + linear_comment_updated_v2: linearCommentUpdatedV2Trigger, + linear_project_created_v2: linearProjectCreatedV2Trigger, + linear_project_updated_v2: linearProjectUpdatedV2Trigger, + linear_cycle_created_v2: linearCycleCreatedV2Trigger, + linear_cycle_updated_v2: linearCycleUpdatedV2Trigger, + linear_label_created_v2: linearLabelCreatedV2Trigger, + linear_label_updated_v2: linearLabelUpdatedV2Trigger, + linear_project_update_created_v2: linearProjectUpdateCreatedV2Trigger, + linear_customer_request_created_v2: linearCustomerRequestCreatedV2Trigger, + linear_customer_request_updated_v2: linearCustomerRequestUpdatedV2Trigger, microsoftteams_webhook: microsoftTeamsWebhookTrigger, microsoftteams_chat_subscription: microsoftTeamsChatSubscriptionTrigger, outlook_poller: outlookPollingTrigger, From 7351b43fbbece8160a97662fd39fdfed566bbba4 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Mon, 6 Apr 2026 11:36:56 -0700 Subject: [PATCH 2/5] fix(triggers): preserve specific Linear API error messages in catch block --- apps/sim/lib/webhooks/providers/linear.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/sim/lib/webhooks/providers/linear.ts b/apps/sim/lib/webhooks/providers/linear.ts index 2e0889bb75e..3e1173641df 100644 --- a/apps/sim/lib/webhooks/providers/linear.ts +++ b/apps/sim/lib/webhooks/providers/linear.ts @@ -158,7 +158,7 @@ export const linearHandler: WebhookProviderHandler = { }, } } catch (error) { - if (error instanceof Error && error.message.includes('API key')) { + if (error instanceof Error && error.message !== 'fetch failed') { throw error } logger.error(`[${ctx.requestId}] Error creating Linear webhook`, { From fd7c85650d4d51712ddb50051ec6297cea42acab Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Mon, 6 Apr 2026 11:46:12 -0700 Subject: [PATCH 3/5] fix(triggers): check response.ok before JSON parsing, replace as any with as unknown --- apps/sim/lib/webhooks/providers/linear.ts | 13 +++++++++++++ apps/sim/triggers/linear/utils.ts | 14 +++++++------- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/apps/sim/lib/webhooks/providers/linear.ts b/apps/sim/lib/webhooks/providers/linear.ts index 3e1173641df..33bb24c13d7 100644 --- a/apps/sim/lib/webhooks/providers/linear.ts +++ b/apps/sim/lib/webhooks/providers/linear.ts @@ -134,6 +134,12 @@ export const linearHandler: WebhookProviderHandler = { }), }) + if (!response.ok) { + throw new Error( + `Linear API returned HTTP ${response.status}. Please verify your API key and try again.` + ) + } + const data = await response.json() const result = data?.data?.webhookCreate @@ -192,6 +198,13 @@ export const linearHandler: WebhookProviderHandler = { }), }) + if (!response.ok) { + logger.warn( + `[${ctx.requestId}] Linear API returned HTTP ${response.status} during webhook deletion for ${externalId}` + ) + return + } + const data = await response.json() if (data?.data?.webhookDelete?.success) { logger.info( diff --git a/apps/sim/triggers/linear/utils.ts b/apps/sim/triggers/linear/utils.ts index 56f60d07059..ffc2ab0c385 100644 --- a/apps/sim/triggers/linear/utils.ts +++ b/apps/sim/triggers/linear/utils.ts @@ -434,7 +434,7 @@ export function buildIssueOutputs(): Record { type: 'object', description: 'Previous values for changed fields (only present on update)', }, - } as any + } as unknown as Record } /** @@ -521,7 +521,7 @@ export function buildCommentOutputs(): Record { type: 'object', description: 'Previous values for changed fields (only present on update)', }, - } as any + } as unknown as Record } /** @@ -664,7 +664,7 @@ export function buildProjectOutputs(): Record { type: 'object', description: 'Previous values for changed fields (only present on update)', }, - } as any + } as unknown as Record } /** @@ -767,7 +767,7 @@ export function buildCycleOutputs(): Record { type: 'object', description: 'Previous values for changed fields (only present on update)', }, - } as any + } as unknown as Record } /** @@ -854,7 +854,7 @@ export function buildLabelOutputs(): Record { type: 'object', description: 'Previous values for changed fields (only present on update)', }, - } as any + } as unknown as Record } /** @@ -929,7 +929,7 @@ export function buildProjectUpdateOutputs(): Record { type: 'object', description: 'Previous values for changed fields (only present on update)', }, - } as any + } as unknown as Record } /** @@ -1012,7 +1012,7 @@ export function buildCustomerRequestOutputs(): Record { type: 'object', description: 'Previous values for changed fields (only present on update)', }, - } as any + } as unknown as Record } /** From 4d1094f5373211c95f559e0ec05d9e38a864e1ee Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Mon, 6 Apr 2026 13:14:25 -0700 Subject: [PATCH 4/5] fix linear subscription params --- apps/sim/lib/core/config/feature-flags.ts | 2 +- apps/sim/lib/webhooks/providers/linear.ts | 23 +++++++++++++++-------- apps/sim/triggers/linear/utils.ts | 10 ++++++++++ 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/apps/sim/lib/core/config/feature-flags.ts b/apps/sim/lib/core/config/feature-flags.ts index 7aeb4c33071..08bb0957384 100644 --- a/apps/sim/lib/core/config/feature-flags.ts +++ b/apps/sim/lib/core/config/feature-flags.ts @@ -29,7 +29,7 @@ try { } catch { // invalid URL — isHosted stays false } -export const isHosted = appHostname === 'sim.ai' || appHostname.endsWith('.sim.ai') +export const isHosted = true /** * Is billing enforcement enabled diff --git a/apps/sim/lib/webhooks/providers/linear.ts b/apps/sim/lib/webhooks/providers/linear.ts index 33bb24c13d7..7df343dfc6f 100644 --- a/apps/sim/lib/webhooks/providers/linear.ts +++ b/apps/sim/lib/webhooks/providers/linear.ts @@ -108,6 +108,20 @@ export const linearHandler: WebhookProviderHandler = { const notificationUrl = getNotificationUrl(ctx.webhook) const webhookSecret = generateId() + const teamId = config.teamId as string | undefined + + const input: Record = { + url: notificationUrl, + resourceTypes, + secret: webhookSecret, + enabled: true, + } + + if (teamId) { + input.teamId = teamId + } else { + input.allPublicTeams = true + } try { const response = await fetch('https://api.linear.app/graphql', { @@ -123,14 +137,7 @@ export const linearHandler: WebhookProviderHandler = { webhook { id enabled } } }`, - variables: { - input: { - url: notificationUrl, - resourceTypes, - secret: webhookSecret, - enabled: true, - }, - }, + variables: { input }, }), }) diff --git a/apps/sim/triggers/linear/utils.ts b/apps/sim/triggers/linear/utils.ts index ffc2ab0c385..788a6f22127 100644 --- a/apps/sim/triggers/linear/utils.ts +++ b/apps/sim/triggers/linear/utils.ts @@ -84,6 +84,7 @@ export function linearSetupInstructions(eventType: string, additionalNotes?: str export function linearV2SetupInstructions(eventType: string, additionalNotes?: string): string { const instructions = [ 'Enter your Linear API Key above. You can create one in Linear at Settings > API > Personal API keys.', + 'Optionally enter a Team ID to scope the webhook to a single team. Leave it empty to receive events from all public teams. You can find Team IDs in Linear under Settings > Teams or via the API.', `Click "Save Configuration" to automatically create the webhook in Linear for ${eventType} events.`, 'The webhook will be automatically deleted when you remove this trigger.', ] @@ -160,6 +161,15 @@ export function buildLinearV2SubBlocks(options: { condition: { field: 'selectedTriggerId', value: triggerId }, }) + blocks.push({ + id: 'teamId', + title: 'Team ID', + type: 'short-input', + placeholder: 'All teams (optional)', + mode: 'trigger', + condition: { field: 'selectedTriggerId', value: triggerId }, + }) + blocks.push({ id: 'triggerSave', title: '', From f6ea743e347331f0fba96964f12bd98a46c5b669 Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Mon, 6 Apr 2026 13:35:35 -0700 Subject: [PATCH 5/5] fix build --- apps/sim/lib/core/config/feature-flags.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/sim/lib/core/config/feature-flags.ts b/apps/sim/lib/core/config/feature-flags.ts index 08bb0957384..7aeb4c33071 100644 --- a/apps/sim/lib/core/config/feature-flags.ts +++ b/apps/sim/lib/core/config/feature-flags.ts @@ -29,7 +29,7 @@ try { } catch { // invalid URL — isHosted stays false } -export const isHosted = true +export const isHosted = appHostname === 'sim.ai' || appHostname.endsWith('.sim.ai') /** * Is billing enforcement enabled