diff --git a/apps/sim/app/(landing)/integrations/data/integrations.json b/apps/sim/app/(landing)/integrations/data/integrations.json index a05fcbb7ef..13ef350765 100644 --- a/apps/sim/app/(landing)/integrations/data/integrations.json +++ b/apps/sim/app/(landing)/integrations/data/integrations.json @@ -10784,8 +10784,34 @@ } ], "operationCount": 4, - "triggers": [], - "triggerCount": 0, + "triggers": [ + { + "id": "servicenow_incident_created", + "name": "ServiceNow Incident Created", + "description": "Trigger workflow when a new incident is created in ServiceNow" + }, + { + "id": "servicenow_incident_updated", + "name": "ServiceNow Incident Updated", + "description": "Trigger workflow when an incident is updated in ServiceNow" + }, + { + "id": "servicenow_change_request_created", + "name": "ServiceNow Change Request Created", + "description": "Trigger workflow when a new change request is created in ServiceNow" + }, + { + "id": "servicenow_change_request_updated", + "name": "ServiceNow Change Request Updated", + "description": "Trigger workflow when a change request is updated in ServiceNow" + }, + { + "id": "servicenow_webhook", + "name": "ServiceNow Webhook (All Events)", + "description": "Trigger workflow on any ServiceNow webhook event" + } + ], + "triggerCount": 5, "authType": "none", "category": "tools", "integrationType": "customer-support", diff --git a/apps/sim/blocks/blocks/servicenow.ts b/apps/sim/blocks/blocks/servicenow.ts index 1437658472..06e7249f30 100644 --- a/apps/sim/blocks/blocks/servicenow.ts +++ b/apps/sim/blocks/blocks/servicenow.ts @@ -2,6 +2,7 @@ import { ServiceNowIcon } from '@/components/icons' import type { BlockConfig } from '@/blocks/types' import { IntegrationType } from '@/blocks/types' import type { ServiceNowResponse } from '@/tools/servicenow/types' +import { getTrigger } from '@/triggers' export const ServiceNowBlock: BlockConfig = { type: 'servicenow', @@ -215,6 +216,11 @@ Output: {"state": "2", "assigned_to": "john.doe", "work_notes": "Assigned and st condition: { field: 'operation', value: 'servicenow_delete_record' }, required: true, }, + ...getTrigger('servicenow_incident_created').subBlocks, + ...getTrigger('servicenow_incident_updated').subBlocks, + ...getTrigger('servicenow_change_request_created').subBlocks, + ...getTrigger('servicenow_change_request_updated').subBlocks, + ...getTrigger('servicenow_webhook').subBlocks, ], tools: { access: [ @@ -262,4 +268,14 @@ Output: {"state": "2", "assigned_to": "john.doe", "work_notes": "Assigned and st success: { type: 'boolean', description: 'Operation success status' }, metadata: { type: 'json', description: 'Operation metadata' }, }, + triggers: { + enabled: true, + available: [ + 'servicenow_incident_created', + 'servicenow_incident_updated', + 'servicenow_change_request_created', + 'servicenow_change_request_updated', + 'servicenow_webhook', + ], + }, } diff --git a/apps/sim/lib/webhooks/providers/registry.ts b/apps/sim/lib/webhooks/providers/registry.ts index 789546a755..332add6598 100644 --- a/apps/sim/lib/webhooks/providers/registry.ts +++ b/apps/sim/lib/webhooks/providers/registry.ts @@ -28,6 +28,7 @@ import { outlookHandler } from '@/lib/webhooks/providers/outlook' import { resendHandler } from '@/lib/webhooks/providers/resend' import { rssHandler } from '@/lib/webhooks/providers/rss' import { salesforceHandler } from '@/lib/webhooks/providers/salesforce' +import { servicenowHandler } from '@/lib/webhooks/providers/servicenow' import { slackHandler } from '@/lib/webhooks/providers/slack' import { stripeHandler } from '@/lib/webhooks/providers/stripe' import { telegramHandler } from '@/lib/webhooks/providers/telegram' @@ -72,6 +73,7 @@ const PROVIDER_HANDLERS: Record = { outlook: outlookHandler, rss: rssHandler, salesforce: salesforceHandler, + servicenow: servicenowHandler, slack: slackHandler, stripe: stripeHandler, telegram: telegramHandler, diff --git a/apps/sim/lib/webhooks/providers/servicenow.ts b/apps/sim/lib/webhooks/providers/servicenow.ts new file mode 100644 index 0000000000..8118bd72ed --- /dev/null +++ b/apps/sim/lib/webhooks/providers/servicenow.ts @@ -0,0 +1,57 @@ +import { createLogger } from '@sim/logger' +import { NextResponse } from 'next/server' +import type { + AuthContext, + EventMatchContext, + WebhookProviderHandler, +} from '@/lib/webhooks/providers/types' +import { verifyTokenAuth } from '@/lib/webhooks/providers/utils' + +const logger = createLogger('WebhookProvider:ServiceNow') + +function asRecord(body: unknown): Record { + return body && typeof body === 'object' && !Array.isArray(body) + ? (body as Record) + : {} +} + +export const servicenowHandler: WebhookProviderHandler = { + verifyAuth({ request, requestId, providerConfig }: AuthContext): NextResponse | null { + const secret = providerConfig.webhookSecret as string | undefined + if (!secret?.trim()) { + logger.warn(`[${requestId}] ServiceNow webhook missing webhookSecret — rejecting`) + return new NextResponse('Unauthorized - Webhook secret not configured', { status: 401 }) + } + + if ( + !verifyTokenAuth(request, secret.trim(), 'x-sim-webhook-secret') && + !verifyTokenAuth(request, secret.trim()) + ) { + logger.warn(`[${requestId}] ServiceNow webhook secret verification failed`) + return new NextResponse('Unauthorized - Invalid webhook secret', { status: 401 }) + } + + return null + }, + + async matchEvent({ webhook, workflow, body, requestId, providerConfig }: EventMatchContext) { + const triggerId = providerConfig.triggerId as string | undefined + if (!triggerId) { + return true + } + + const { isServiceNowEventMatch } = await import('@/triggers/servicenow/utils') + const configuredTableName = providerConfig.tableName as string | undefined + const obj = asRecord(body) + + if (!isServiceNowEventMatch(triggerId, obj, configuredTableName)) { + logger.debug( + `[${requestId}] ServiceNow event mismatch for trigger ${triggerId}. Skipping execution.`, + { webhookId: webhook.id, workflowId: workflow.id, triggerId } + ) + return false + } + + return true + }, +} diff --git a/apps/sim/triggers/registry.ts b/apps/sim/triggers/registry.ts index 6db1582457..1e7cf2b3c8 100644 --- a/apps/sim/triggers/registry.ts +++ b/apps/sim/triggers/registry.ts @@ -235,6 +235,13 @@ import { salesforceRecordUpdatedTrigger, salesforceWebhookTrigger, } from '@/triggers/salesforce' +import { + servicenowChangeRequestCreatedTrigger, + servicenowChangeRequestUpdatedTrigger, + servicenowIncidentCreatedTrigger, + servicenowIncidentUpdatedTrigger, + servicenowWebhookTrigger, +} from '@/triggers/servicenow' import { slackWebhookTrigger } from '@/triggers/slack' import { stripeWebhookTrigger } from '@/triggers/stripe' import { telegramWebhookTrigger } from '@/triggers/telegram' @@ -437,6 +444,11 @@ export const TRIGGER_REGISTRY: TriggerRegistry = { salesforce_opportunity_stage_changed: salesforceOpportunityStageChangedTrigger, salesforce_case_status_changed: salesforceCaseStatusChangedTrigger, salesforce_webhook: salesforceWebhookTrigger, + servicenow_incident_created: servicenowIncidentCreatedTrigger, + servicenow_incident_updated: servicenowIncidentUpdatedTrigger, + servicenow_change_request_created: servicenowChangeRequestCreatedTrigger, + servicenow_change_request_updated: servicenowChangeRequestUpdatedTrigger, + servicenow_webhook: servicenowWebhookTrigger, stripe_webhook: stripeWebhookTrigger, telegram_webhook: telegramWebhookTrigger, typeform_webhook: typeformWebhookTrigger, diff --git a/apps/sim/triggers/servicenow/change_request_created.ts b/apps/sim/triggers/servicenow/change_request_created.ts new file mode 100644 index 0000000000..bd538158dd --- /dev/null +++ b/apps/sim/triggers/servicenow/change_request_created.ts @@ -0,0 +1,37 @@ +import { ServiceNowIcon } from '@/components/icons' +import { buildTriggerSubBlocks } from '@/triggers' +import { + buildChangeRequestOutputs, + buildServiceNowExtraFields, + servicenowSetupInstructions, + servicenowTriggerOptions, +} from '@/triggers/servicenow/utils' +import type { TriggerConfig } from '@/triggers/types' + +/** + * ServiceNow Change Request Created Trigger + */ +export const servicenowChangeRequestCreatedTrigger: TriggerConfig = { + id: 'servicenow_change_request_created', + name: 'ServiceNow Change Request Created', + provider: 'servicenow', + description: 'Trigger workflow when a new change request is created in ServiceNow', + version: '1.0.0', + icon: ServiceNowIcon, + + subBlocks: buildTriggerSubBlocks({ + triggerId: 'servicenow_change_request_created', + triggerOptions: servicenowTriggerOptions, + setupInstructions: servicenowSetupInstructions('Insert (record creation)'), + extraFields: buildServiceNowExtraFields('servicenow_change_request_created'), + }), + + outputs: buildChangeRequestOutputs(), + + webhook: { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, +} diff --git a/apps/sim/triggers/servicenow/change_request_updated.ts b/apps/sim/triggers/servicenow/change_request_updated.ts new file mode 100644 index 0000000000..f7148f9058 --- /dev/null +++ b/apps/sim/triggers/servicenow/change_request_updated.ts @@ -0,0 +1,37 @@ +import { ServiceNowIcon } from '@/components/icons' +import { buildTriggerSubBlocks } from '@/triggers' +import { + buildChangeRequestOutputs, + buildServiceNowExtraFields, + servicenowSetupInstructions, + servicenowTriggerOptions, +} from '@/triggers/servicenow/utils' +import type { TriggerConfig } from '@/triggers/types' + +/** + * ServiceNow Change Request Updated Trigger + */ +export const servicenowChangeRequestUpdatedTrigger: TriggerConfig = { + id: 'servicenow_change_request_updated', + name: 'ServiceNow Change Request Updated', + provider: 'servicenow', + description: 'Trigger workflow when a change request is updated in ServiceNow', + version: '1.0.0', + icon: ServiceNowIcon, + + subBlocks: buildTriggerSubBlocks({ + triggerId: 'servicenow_change_request_updated', + triggerOptions: servicenowTriggerOptions, + setupInstructions: servicenowSetupInstructions('Update (record modification)'), + extraFields: buildServiceNowExtraFields('servicenow_change_request_updated'), + }), + + outputs: buildChangeRequestOutputs(), + + webhook: { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, +} diff --git a/apps/sim/triggers/servicenow/incident_created.ts b/apps/sim/triggers/servicenow/incident_created.ts new file mode 100644 index 0000000000..170ab35729 --- /dev/null +++ b/apps/sim/triggers/servicenow/incident_created.ts @@ -0,0 +1,40 @@ +import { ServiceNowIcon } from '@/components/icons' +import { buildTriggerSubBlocks } from '@/triggers' +import { + buildIncidentOutputs, + buildServiceNowExtraFields, + servicenowSetupInstructions, + servicenowTriggerOptions, +} from '@/triggers/servicenow/utils' +import type { TriggerConfig } from '@/triggers/types' + +/** + * ServiceNow Incident Created Trigger + * + * Primary trigger — includes the dropdown for selecting trigger type. + */ +export const servicenowIncidentCreatedTrigger: TriggerConfig = { + id: 'servicenow_incident_created', + name: 'ServiceNow Incident Created', + provider: 'servicenow', + description: 'Trigger workflow when a new incident is created in ServiceNow', + version: '1.0.0', + icon: ServiceNowIcon, + + subBlocks: buildTriggerSubBlocks({ + triggerId: 'servicenow_incident_created', + triggerOptions: servicenowTriggerOptions, + includeDropdown: true, + setupInstructions: servicenowSetupInstructions('Insert (record creation)'), + extraFields: buildServiceNowExtraFields('servicenow_incident_created'), + }), + + outputs: buildIncidentOutputs(), + + webhook: { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, +} diff --git a/apps/sim/triggers/servicenow/incident_updated.ts b/apps/sim/triggers/servicenow/incident_updated.ts new file mode 100644 index 0000000000..70c9914d6a --- /dev/null +++ b/apps/sim/triggers/servicenow/incident_updated.ts @@ -0,0 +1,37 @@ +import { ServiceNowIcon } from '@/components/icons' +import { buildTriggerSubBlocks } from '@/triggers' +import { + buildIncidentOutputs, + buildServiceNowExtraFields, + servicenowSetupInstructions, + servicenowTriggerOptions, +} from '@/triggers/servicenow/utils' +import type { TriggerConfig } from '@/triggers/types' + +/** + * ServiceNow Incident Updated Trigger + */ +export const servicenowIncidentUpdatedTrigger: TriggerConfig = { + id: 'servicenow_incident_updated', + name: 'ServiceNow Incident Updated', + provider: 'servicenow', + description: 'Trigger workflow when an incident is updated in ServiceNow', + version: '1.0.0', + icon: ServiceNowIcon, + + subBlocks: buildTriggerSubBlocks({ + triggerId: 'servicenow_incident_updated', + triggerOptions: servicenowTriggerOptions, + setupInstructions: servicenowSetupInstructions('Update (record modification)'), + extraFields: buildServiceNowExtraFields('servicenow_incident_updated'), + }), + + outputs: buildIncidentOutputs(), + + webhook: { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, +} diff --git a/apps/sim/triggers/servicenow/index.ts b/apps/sim/triggers/servicenow/index.ts new file mode 100644 index 0000000000..adb585ff29 --- /dev/null +++ b/apps/sim/triggers/servicenow/index.ts @@ -0,0 +1,5 @@ +export { servicenowChangeRequestCreatedTrigger } from './change_request_created' +export { servicenowChangeRequestUpdatedTrigger } from './change_request_updated' +export { servicenowIncidentCreatedTrigger } from './incident_created' +export { servicenowIncidentUpdatedTrigger } from './incident_updated' +export { servicenowWebhookTrigger } from './webhook' diff --git a/apps/sim/triggers/servicenow/utils.ts b/apps/sim/triggers/servicenow/utils.ts new file mode 100644 index 0000000000..7c85b65961 --- /dev/null +++ b/apps/sim/triggers/servicenow/utils.ts @@ -0,0 +1,280 @@ +import type { SubBlockConfig } from '@/blocks/types' +import type { TriggerOutput } from '@/triggers/types' + +/** + * Shared trigger dropdown options for all ServiceNow triggers + */ +export const servicenowTriggerOptions = [ + { label: 'Incident Created', id: 'servicenow_incident_created' }, + { label: 'Incident Updated', id: 'servicenow_incident_updated' }, + { label: 'Change Request Created', id: 'servicenow_change_request_created' }, + { label: 'Change Request Updated', id: 'servicenow_change_request_updated' }, + { label: 'Generic Webhook (All Events)', id: 'servicenow_webhook' }, +] + +/** + * Generates setup instructions for ServiceNow webhooks. + * ServiceNow uses Business Rules with RESTMessageV2 for outbound webhooks. + */ +export function servicenowSetupInstructions(eventType: string): string { + const instructions = [ + 'Note: You need admin or developer permissions in your ServiceNow instance to create Business Rules.', + 'Navigate to System Definition > Business Rules and create a new Business Rule.', + `Set the table (e.g., incident, change_request), set When to after, and check ${eventType}.`, + 'Check the Advanced checkbox to enable the script editor.', + 'Copy the Webhook URL above and generate a Webhook Secret (any strong random string). Paste the secret in the Webhook Secret field here.', + `In the script, use RESTMessageV2 to POST the record data as JSON to the Webhook URL above. Include the secret as Authorization: Bearer <your secret> or X-Sim-Webhook-Secret: <your secret>. Example:
var r = new sn_ws.RESTMessageV2();\nr.setEndpoint("<webhook_url>");\nr.setHttpMethod("POST");\nr.setRequestHeader("Content-Type", "application/json");\nr.setRequestHeader("Authorization", "Bearer <your_webhook_secret>");\nr.setRequestBody(JSON.stringify({\n sysId: current.sys_id.toString(),\n number: current.number.toString(),\n shortDescription: current.short_description.toString(),\n state: current.state.toString(),\n priority: current.priority.toString()\n}));\nr.execute();`, + 'Activate the Business Rule and click "Save" above to activate your trigger.', + ] + + return instructions + .map( + (instruction, index) => + `
${index === 0 ? instruction : `${index}. ${instruction}`}
` + ) + .join('') +} + +/** + * Webhook secret field for ServiceNow triggers + */ +function servicenowWebhookSecretField(triggerId: string): SubBlockConfig { + return { + id: 'webhookSecret', + title: 'Webhook Secret', + type: 'short-input', + placeholder: 'Generate a secret and paste it here', + description: + 'Required. Use the same value in your ServiceNow Business Rule as Bearer token or X-Sim-Webhook-Secret.', + password: true, + required: true, + mode: 'trigger', + condition: { field: 'selectedTriggerId', value: triggerId }, + } +} + +/** + * Extra fields for ServiceNow triggers (webhook secret + optional table filter) + */ +export function buildServiceNowExtraFields(triggerId: string): SubBlockConfig[] { + return [ + servicenowWebhookSecretField(triggerId), + { + id: 'tableName', + title: 'Table Name (Optional)', + type: 'short-input', + placeholder: 'e.g., incident, change_request', + description: 'Optionally filter to a specific ServiceNow table', + mode: 'trigger', + condition: { field: 'selectedTriggerId', value: triggerId }, + }, + ] +} + +/** + * Common record fields shared across ServiceNow trigger outputs + */ +function buildRecordOutputs(): Record { + return { + sysId: { type: 'string', description: 'Unique system ID of the record' }, + number: { type: 'string', description: 'Record number (e.g., INC0010001, CHG0010001)' }, + tableName: { type: 'string', description: 'ServiceNow table name' }, + shortDescription: { type: 'string', description: 'Short description of the record' }, + description: { type: 'string', description: 'Full description of the record' }, + state: { type: 'string', description: 'Current state of the record' }, + priority: { + type: 'string', + description: 'Priority level (1=Critical, 2=High, 3=Moderate, 4=Low, 5=Planning)', + }, + assignedTo: { type: 'string', description: 'User assigned to this record' }, + assignmentGroup: { type: 'string', description: 'Group assigned to this record' }, + createdBy: { type: 'string', description: 'User who created the record' }, + createdOn: { type: 'string', description: 'When the record was created (ISO 8601)' }, + updatedBy: { type: 'string', description: 'User who last updated the record' }, + updatedOn: { type: 'string', description: 'When the record was last updated (ISO 8601)' }, + } +} + +/** + * Outputs for incident triggers + */ +export function buildIncidentOutputs(): Record { + return { + ...buildRecordOutputs(), + urgency: { type: 'string', description: 'Urgency level (1=High, 2=Medium, 3=Low)' }, + impact: { type: 'string', description: 'Impact level (1=High, 2=Medium, 3=Low)' }, + category: { type: 'string', description: 'Incident category' }, + subcategory: { type: 'string', description: 'Incident subcategory' }, + caller: { type: 'string', description: 'Caller/requester of the incident' }, + resolvedBy: { type: 'string', description: 'User who resolved the incident' }, + resolvedAt: { type: 'string', description: 'When the incident was resolved' }, + closeNotes: { type: 'string', description: 'Notes added when the incident was closed' }, + record: { type: 'json', description: 'Full incident record data' }, + } +} + +/** + * Outputs for change request triggers + */ +export function buildChangeRequestOutputs(): Record { + return { + ...buildRecordOutputs(), + type: { type: 'string', description: 'Change type (Normal, Standard, Emergency)' }, + risk: { type: 'string', description: 'Risk level of the change' }, + impact: { type: 'string', description: 'Impact level of the change' }, + approval: { type: 'string', description: 'Approval status' }, + startDate: { type: 'string', description: 'Planned start date' }, + endDate: { type: 'string', description: 'Planned end date' }, + category: { type: 'string', description: 'Change category' }, + record: { type: 'json', description: 'Full change request record data' }, + } +} + +function normalizeToken(s: string): string { + return s + .trim() + .toLowerCase() + .replace(/[\s-]+/g, '_') +} + +/** + * Extracts the table name from a ServiceNow webhook payload. + * Business Rule scripts can send tableName in multiple formats. + */ +function extractTableName(body: Record): string | undefined { + const candidates = [body.tableName, body.table_name, body.table, body.sys_class_name] + for (const c of candidates) { + if (typeof c === 'string' && c.trim()) { + return c.trim() + } + } + return undefined +} + +/** + * Extracts the event type from a ServiceNow webhook payload. + */ +function extractEventType(body: Record): string | undefined { + const candidates = [body.eventType, body.event_type, body.action, body.operation] + for (const c of candidates) { + if (typeof c === 'string' && c.trim()) { + return c.trim() + } + } + return undefined +} + +const INCIDENT_CREATED = new Set([ + 'incident_created', + 'insert', + 'created', + 'create', + 'after_insert', + 'afterinsert', +]) + +const INCIDENT_UPDATED = new Set([ + 'incident_updated', + 'update', + 'updated', + 'after_update', + 'afterupdate', +]) + +const CHANGE_REQUEST_CREATED = new Set([ + 'change_request_created', + 'insert', + 'created', + 'create', + 'after_insert', + 'afterinsert', +]) + +const CHANGE_REQUEST_UPDATED = new Set([ + 'change_request_updated', + 'update', + 'updated', + 'after_update', + 'afterupdate', +]) + +/** + * Checks whether a ServiceNow webhook payload matches the configured trigger. + * Used by the ServiceNow provider handler to filter events at runtime. + */ +export function isServiceNowEventMatch( + triggerId: string, + body: Record, + configuredTableName?: string +): boolean { + const payloadTable = extractTableName(body) + const eventType = extractEventType(body) + + if (triggerId === 'servicenow_webhook') { + if (!configuredTableName?.trim()) { + return true + } + if (!payloadTable) { + return true + } + return normalizeToken(payloadTable) === normalizeToken(configuredTableName) + } + + if (triggerId === 'servicenow_incident_created' || triggerId === 'servicenow_incident_updated') { + if (configuredTableName?.trim()) { + if (payloadTable && normalizeToken(payloadTable) !== normalizeToken(configuredTableName)) { + return false + } + } else if (payloadTable && normalizeToken(payloadTable) !== 'incident') { + return false + } + + if (!eventType) { + return true + } + + const normalized = normalizeToken(eventType) + return triggerId === 'servicenow_incident_created' + ? INCIDENT_CREATED.has(normalized) + : INCIDENT_UPDATED.has(normalized) + } + + if ( + triggerId === 'servicenow_change_request_created' || + triggerId === 'servicenow_change_request_updated' + ) { + if (configuredTableName?.trim()) { + if (payloadTable && normalizeToken(payloadTable) !== normalizeToken(configuredTableName)) { + return false + } + } else if (payloadTable && normalizeToken(payloadTable) !== 'change_request') { + return false + } + + if (!eventType) { + return true + } + + const normalized = normalizeToken(eventType) + return triggerId === 'servicenow_change_request_created' + ? CHANGE_REQUEST_CREATED.has(normalized) + : CHANGE_REQUEST_UPDATED.has(normalized) + } + + return true +} + +/** + * Outputs for the generic webhook trigger (all events) + */ +export function buildServiceNowWebhookOutputs(): Record { + return { + ...buildRecordOutputs(), + eventType: { + type: 'string', + description: 'The type of event that triggered this workflow (e.g., insert, update, delete)', + }, + category: { type: 'string', description: 'Record category' }, + record: { type: 'json', description: 'Full record data from the webhook payload' }, + } +} diff --git a/apps/sim/triggers/servicenow/webhook.ts b/apps/sim/triggers/servicenow/webhook.ts new file mode 100644 index 0000000000..7cb1d19d5d --- /dev/null +++ b/apps/sim/triggers/servicenow/webhook.ts @@ -0,0 +1,38 @@ +import { ServiceNowIcon } from '@/components/icons' +import { buildTriggerSubBlocks } from '@/triggers' +import { + buildServiceNowExtraFields, + buildServiceNowWebhookOutputs, + servicenowSetupInstructions, + servicenowTriggerOptions, +} from '@/triggers/servicenow/utils' +import type { TriggerConfig } from '@/triggers/types' + +/** + * Generic ServiceNow Webhook Trigger + * Captures all ServiceNow webhook events + */ +export const servicenowWebhookTrigger: TriggerConfig = { + id: 'servicenow_webhook', + name: 'ServiceNow Webhook (All Events)', + provider: 'servicenow', + description: 'Trigger workflow on any ServiceNow webhook event', + version: '1.0.0', + icon: ServiceNowIcon, + + subBlocks: buildTriggerSubBlocks({ + triggerId: 'servicenow_webhook', + triggerOptions: servicenowTriggerOptions, + setupInstructions: servicenowSetupInstructions('Insert, Update, or Delete'), + extraFields: buildServiceNowExtraFields('servicenow_webhook'), + }), + + outputs: buildServiceNowWebhookOutputs(), + + webhook: { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, +}