This workflow corresponds to n8n.io template #11824 — we link there as the canonical source.
This workflow follows the Airtable → Slack recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"id": "rOltbkkDJQrk5R2f",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Snyk Vulnerability Automation Workflow with Webhook, Jira, Slack & Airtable",
"tags": [],
"nodes": [
{
"id": "15e19984-9133-4985-ab4a-bb61c19e1cdf",
"name": "Receive Vulnerability Data",
"type": "n8n-nodes-base.webhook",
"position": [
96,
-976
],
"parameters": {
"path": "snyk-vuln",
"options": {},
"httpMethod": "POST",
"responseMode": "lastNode"
},
"typeVersion": 1
},
{
"id": "2fbdc06e-b08d-4abd-b1d0-ef4af33a94f5",
"name": "Normalize Vulnerability Data",
"type": "n8n-nodes-base.function",
"position": [
320,
-976
],
"parameters": {
"functionCode": "// Access the body of the webhook\nconst body = $json.body || {};\nlet vulns = [];\n\n// Check for different possible shapes\nif (Array.isArray(body.vulnerabilities)) vulns = body.vulnerabilities;\nelse if (Array.isArray(body.issues)) vulns = body.issues;\nelse if (Array.isArray(body.vulns)) vulns = body.vulns;\nelse if (Array.isArray(body)) vulns = body;\nelse if (body.issue) vulns = [body.issue];\n\n// Return normalized array\nreturn (vulns.length ? vulns : [{}]).map(v => ({ json: v }));\n"
},
"typeVersion": 1
},
{
"id": "d0e9d57e-0c84-4dc6-a738-12821bc57367",
"name": "Validate Vulnerability Fields",
"type": "n8n-nodes-base.code",
"position": [
544,
-976
],
"parameters": {
"jsCode": "const results = [];\n\nfor (const item of $input.all()) {\n const raw = item.json;\n\n const id = raw.id || raw.issueId || raw.name || '';\n const title = raw.title || raw.description || id;\n\n const cvssRaw = raw.cvssScore || raw.cvss || raw.cvss_score;\n const cvss = parseFloat(cvssRaw);\n\n const packageName = raw.package || raw.packageName || (raw.pkg && raw.pkg.name) || '';\n const vulnUrl = raw.url || raw.reference || raw.issueUrl || '';\n\n // Force strict boolean\n const isValid =\n id !== '' &&\n title !== '' &&\n !isNaN(cvss) &&\n packageName !== '' &&\n vulnUrl !== '';\n\n results.push({\n json: { id, title, cvss, packageName, vulnUrl, raw, isValid }\n });\n}\n\nreturn results;\n"
},
"typeVersion": 2
},
{
"id": "d9b167fb-b26f-4546-abad-84d3235dd015",
"name": "Check if Vulnerability is Valid",
"type": "n8n-nodes-base.if",
"position": [
768,
-976
],
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{ $json.isValid }}",
"value2": true
}
]
}
},
"typeVersion": 1
},
{
"id": "33e3257e-a5c5-4397-bb32-c9b9bf679f17",
"name": "Notify Malformed Payload",
"type": "n8n-nodes-base.slack",
"position": [
1088,
-832
],
"parameters": {
"text": "=*Malformed Payload Detected*\nSome required fields are missing. Manual review Required.\n```{{$json.raw ? JSON.stringify($json.raw,null,2) : 'N/A'}}```",
"select": "channel",
"channelId": {
"mode": "list",
"value": "C09S57E2JQ2"
},
"otherOptions": {}
},
"credentials": {
"slackApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.3
},
{
"id": "31c3a368-77fa-44ed-9c21-c742718c5d7d",
"name": "Classify Vulnerability Severity",
"type": "n8n-nodes-base.function",
"position": [
1056,
-1136
],
"parameters": {
"functionCode": "const cvss = Number($json.cvss);\nlet severityLabel, severityText;\n\nif (cvss >= 9.0) { severityLabel = 'CVSS-HIGHEST'; severityText = 'Highest'; }\nelse if (cvss >= 7.0) { severityLabel = 'CVSS-HIGH'; severityText = 'High'; }\nelse if (cvss >= 4.0) { severityLabel = 'CVSS-MEDIUM'; severityText = 'Medium'; }\nelse { severityLabel = 'CVSS-LOW'; severityText = 'Low'; }\n\nreturn [{ json: { ...$json, severityLabel, severityText } }];"
},
"typeVersion": 1
},
{
"id": "2b965cb3-fa61-48d6-b0bb-8cef1c377398",
"name": "Generate Vulnerability Key",
"type": "n8n-nodes-base.function",
"position": [
1280,
-1136
],
"parameters": {
"functionCode": "const id = $json.id || ($json.raw && $json.raw.id) || '';\nconst vulnKey = id ? `vuln-${id}` : '';\nreturn [{ json: { ...$json, vulnKey } }];"
},
"typeVersion": 1
},
{
"id": "8693b884-8da8-4ddd-8a4f-2433bb0749cd",
"name": "Check Existing Jira Issue",
"type": "n8n-nodes-base.jira",
"position": [
1504,
-1136
],
"parameters": {
"options": {
"jql": "=project = KAN AND labels IN (\"{{ $json.vulnKey }}\") AND statusCategory != Done\n",
"fields": "*all"
},
"operation": "getAll",
"returnAll": true
},
"credentials": {
"jiraSoftwareCloudApi": {
"name": "<your credential>"
}
},
"typeVersion": 1,
"alwaysOutputData": true
},
{
"id": "5dc08eb5-ce6b-46be-8b5b-dec1d8998893",
"name": "Determine if Duplicate",
"type": "n8n-nodes-base.code",
"position": [
1712,
-1136
],
"parameters": {
"jsCode": "// Original vulnerability data from previous node\nconst original = $items(\"Generate Vulnerability Key\", 0, 0).json;\n\n// Jira search output \u2192 array of issue objects\nconst jiraArray = Array.isArray($input.all()) ? $input.all() : [];\n\n// Filter out empty objects like [{}]\nconst issues = jiraArray.filter(item => Object.keys(item.json).length > 0);\n\n// Extract issue JSON (n8n wraps each item as {json: {...}})\nconst issue = issues.length > 0 ? issues[0].json : null;\n\nreturn [{\n json: {\n ...original,\n isDuplicate: issue !== null,\n jira: issue\n }\n}];\n"
},
"typeVersion": 2
},
{
"id": "bb8d334c-c5db-4d34-ae56-5c8de33cd371",
"name": "Duplicate Found?",
"type": "n8n-nodes-base.if",
"position": [
1920,
-1136
],
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{$json.isDuplicate}}",
"value2": true
}
]
}
},
"typeVersion": 1
},
{
"id": "66a6b37b-41e8-46a0-b178-07d13c598919",
"name": "Update Existing Jira Issue",
"type": "n8n-nodes-base.jira",
"position": [
2160,
-1152
],
"parameters": {
"issueKey": "={{ $json.jira.key }}",
"operation": "update",
"updateFields": {
"labels": "={{ $json.jira.fields.labels }}",
"summary": "={{ $json.jira.fields.summary }}"
}
},
"credentials": {
"jiraSoftwareCloudApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "49619bcc-4183-46cb-882b-432049e0ec27",
"name": "Notify Duplicate Update",
"type": "n8n-nodes-base.slack",
"position": [
2160,
-1344
],
"parameters": {
"text": "=*Vulnerability Already Exists (Updated)*\n*Jira:* https://yourcompany.atlassian.net/browse/{{ $json.jira.key }}}}",
"select": "channel",
"channelId": {
"mode": "list",
"value": "C09S57E2JQ2"
},
"otherOptions": {}
},
"credentials": {
"slackApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.3
},
{
"id": "bf4d240e-c295-4221-b738-bd1f4b3e207d",
"name": "Create New Jira Vulnerability",
"type": "n8n-nodes-base.jira",
"position": [
2160,
-768
],
"parameters": {
"project": {
"mode": "list",
"value": "10000"
},
"summary": "=[VULN] {{ $('Generate Vulnerability Key').item.json.title }} (CVSS: {{ $('Generate Vulnerability Key').item.json.cvss }})",
"issueType": {
"mode": "list",
"value": "10003"
},
"additionalFields": {
"labels": "={{ [$('Generate Vulnerability Key').item.json.vulnKey] }}",
"description": "=Vulnerability Key: {{ $('Generate Vulnerability Key').item.json.vulnKey }}\nVulnerability Issue: {{ $('Generate Vulnerability Key').item.json.title }}\nSeverity: {{ $('Generate Vulnerability Key').item.json.severityText }}\nCVSS:{{ $('Generate Vulnerability Key').item.json.cvss }}\nURL: {{ $('Generate Vulnerability Key').item.json.raw.url }}",
"customFieldsUi": {
"customFieldsValues": [
{
"fieldId": {
"__rl": true,
"mode": "list",
"value": "customfield_10035",
"cachedResultName": "Vulnerability"
},
"fieldValue": "={{ $('Generate Vulnerability Key').item.json.id }}"
}
]
}
}
},
"credentials": {
"jiraSoftwareCloudApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "8a567df7-14c1-442b-bdf7-658df273736b",
"name": "Notify New Vulnerability",
"type": "n8n-nodes-base.slack",
"position": [
2400,
-768
],
"parameters": {
"text": "=*New Vulnerability Created*\n*Title:* {{ $('Classify Vulnerability Severity').item.json.title }}\n*CVSS:* {{ $('Classify Vulnerability Severity').item.json.cvss }}\n*Severity:* {{ $('Classify Vulnerability Severity').item.json.severityText }}\n*Package:* {{ $('Classify Vulnerability Severity').item.json.packageName }}\n*Jira:* https://mycompany.atlassian.net/browse/{{$node[\"Create New Jira Vulnerability\"].json.key}}\n*Details:* {{ $('Classify Vulnerability Severity').item.json.vulnUrl }}",
"select": "channel",
"channelId": {
"mode": "list",
"value": "C09S57E2JQ2"
},
"otherOptions": {}
},
"credentials": {
"slackApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.3
},
{
"id": "0a675201-2cb9-47fe-8510-03c2132d7699",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-16,
-1056
],
"parameters": {
"color": 7,
"width": 272,
"height": 240,
"content": "Entry point webhook that receives vulnerability payload from Snyk or other source"
},
"typeVersion": 1
},
{
"id": "2f490329-2fd2-4287-a378-d7c6f081ba28",
"name": "Create a record",
"type": "n8n-nodes-base.airtable",
"position": [
2656,
-768
],
"parameters": {
"base": {
"__rl": true,
"mode": "list",
"value": "appF2iYPgVqqyXDC1",
"cachedResultUrl": "https://airtable.com/appF2iYPgVqqyXDC1",
"cachedResultName": "n8n Demo"
},
"table": {
"__rl": true,
"mode": "list",
"value": "tblzOTBGc2PYkSM28",
"cachedResultUrl": "https://airtable.com/appF2iYPgVqqyXDC1/tblzOTBGc2PYkSM28",
"cachedResultName": "Vulnerability"
},
"columns": {
"value": {
"CVSS": "={{ $('Generate Vulnerability Key').item.json.cvss }}",
"Title": "={{ $('Generate Vulnerability Key').item.json.title }}",
"VulnURL": "={{ $('Generate Vulnerability Key').item.json.vulnUrl }}",
"Severity": "={{ $('Generate Vulnerability Key').item.json.severityText }}",
"PackageName": "={{ $('Generate Vulnerability Key').item.json.packageName }}",
"Vulnerability key": "={{ $('Generate Vulnerability Key').item.json.vulnKey }}"
},
"schema": [
{
"id": "Title",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Title",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "CVSS",
"type": "number",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "CVSS",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "PackageName",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "PackageName",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "VulnURL",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "VulnURL",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Vulnerability key",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Vulnerability key",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Severity",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Severity",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Attachment Summary",
"type": "string",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "Attachment Summary",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "create"
},
"credentials": {
"airtableTokenApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "ae6751c6-b681-4310-b01a-68c3e53eb65f",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
272,
-1152
],
"parameters": {
"color": 7,
"width": 416,
"height": 336,
"content": "## Normalize & Validate Vulnerability Data\nEnsures all incoming vulnerability data is converted into a consistent format and checks that required fields like ID, title, CVSS, package, and URL are present and valid."
},
"typeVersion": 1
},
{
"id": "516b2134-4f02-490f-80cc-1d41838a61b7",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
704,
-1088
],
"parameters": {
"color": 7,
"width": 246,
"height": 272,
"content": "Valid vulnerabilities continue automatically; invalid ones require manual review."
},
"typeVersion": 1
},
{
"id": "d8c29333-6d89-4b58-8f3f-82b291c27f3c",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
992,
-896
],
"parameters": {
"color": 7,
"width": 288,
"height": 256,
"content": "Sends Slack alert if payload is missing required fields"
},
"typeVersion": 1
},
{
"id": "a767d3ee-b995-488c-b0bb-5a2c925c8146",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
992,
-1344
],
"parameters": {
"color": 7,
"width": 1040,
"height": 416,
"content": "## Vulnerability Deduplication & Tracking\n\nThis process assigns severity labels to vulnerabilities, creates a unique key for each, checks Jira for duplicates, marks duplicates if found, and either updates existing issues or creates new ones."
},
"typeVersion": 1
},
{
"id": "ad6781f4-7684-469a-a533-2e95d2c83915",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
2096,
-1536
],
"parameters": {
"color": 7,
"width": 464,
"height": 560,
"content": "## Handling New Vulnerabilities\nWhen a vulnerability already exists in Jira, the system updates the existing Jira ticket with the latest details and sends a Slack message to let the team know about the update."
},
"typeVersion": 1
},
{
"id": "44e88dfa-f999-4401-8816-7536ba142bb0",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
2112,
-896
],
"parameters": {
"color": 7,
"width": 896,
"height": 320,
"content": "## Handling New Vulnerabilities\nWhen a vulnerability is new, the system creates a fresh Jira ticket for it, sends a Slack message to notify the team, and saves the details in Airtable for tracking."
},
"typeVersion": 1
},
{
"id": "53255fed-48b3-4c5c-9939-e4afb737e742",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
-960,
-1776
],
"parameters": {
"width": 608,
"height": 704,
"content": "## How It Works\nThis workflow takes security issues coming from Snyk and automatically handles them in Jira. First, it cleans and checks the incoming data to make sure all required details are present. Then it calculates the severity level and creates a unique ID for each vulnerability.\nNext, it looks in Jira to see if the same issue already exists.\n\nIf it\u2019s new, the workflow creates a fresh Jira ticket, sends a Slack alert, and stores the data in Airtable.\nIf it already exists, the workflow updates the existing Jira ticket and sends a Slack notification to let the team know.\nIf the incoming data is incomplete, it sends a Slack message for manual review.\nOverall, it fully automates the tracking of vulnerabilities with minimal human effort.\n\n## Setup Steps\n**1.** Connect Snyk webhook to the \u201cReceive Vulnerability\u201d node.\n\n**2.** Verify Normalize + Validation nodes are mapping fields correctly.\n\n**3.** Update severity logic or field names if needed.\n\n**4.** In Jira nodes, set the correct project, issue type, and custom fields.\n\n**5.** Confirm Airtable API key/table mapping (if used).\n\n**6.** Add Slack credentials and choose the alert channel.\n\n**7.** Test the workflow using sample payloads and verify both \u201cnew\u201d and \u201cduplicate\u201d paths.\n\n**8.** When if data is new add on Airtable database\n\n**9.** Activate the workflow once both branches work correctly."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "77f768ec-2c81-4b3b-8e98-2d2b2a05b3f0",
"connections": {
"Duplicate Found?": {
"main": [
[
{
"node": "Update Existing Jira Issue",
"type": "main",
"index": 0
},
{
"node": "Notify Duplicate Update",
"type": "main",
"index": 0
}
],
[
{
"node": "Create New Jira Vulnerability",
"type": "main",
"index": 0
}
]
]
},
"Determine if Duplicate": {
"main": [
[
{
"node": "Duplicate Found?",
"type": "main",
"index": 0
}
]
]
},
"Notify New Vulnerability": {
"main": [
[
{
"node": "Create a record",
"type": "main",
"index": 0
}
]
]
},
"Check Existing Jira Issue": {
"main": [
[
{
"node": "Determine if Duplicate",
"type": "main",
"index": 0
}
]
]
},
"Generate Vulnerability Key": {
"main": [
[
{
"node": "Check Existing Jira Issue",
"type": "main",
"index": 0
}
]
]
},
"Receive Vulnerability Data": {
"main": [
[
{
"node": "Normalize Vulnerability Data",
"type": "main",
"index": 0
}
]
]
},
"Normalize Vulnerability Data": {
"main": [
[
{
"node": "Validate Vulnerability Fields",
"type": "main",
"index": 0
}
]
]
},
"Create New Jira Vulnerability": {
"main": [
[
{
"node": "Notify New Vulnerability",
"type": "main",
"index": 0
}
]
]
},
"Validate Vulnerability Fields": {
"main": [
[
{
"node": "Check if Vulnerability is Valid",
"type": "main",
"index": 0
}
]
]
},
"Check if Vulnerability is Valid": {
"main": [
[
{
"node": "Classify Vulnerability Severity",
"type": "main",
"index": 0
}
],
[
{
"node": "Notify Malformed Payload",
"type": "main",
"index": 0
}
]
]
},
"Classify Vulnerability Severity": {
"main": [
[
{
"node": "Generate Vulnerability Key",
"type": "main",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
airtableTokenApijiraSoftwareCloudApislackApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow receives vulnerability data(e.g., Snyk, Dependabot or any security scanner) from Snyk through a webhook, standardizes and validates the payload, checks Jira for duplicates using a unique vulnerability key, and either updates an existing Jira issue or creates a new…
Source: https://n8n.io/workflows/11824/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Are you tired of the repetitive dance between git push, creating a pull request in GitHub, updating the corresponding task in JIRA, and then manually notifying your team in Slack, or Notion?
This workflow is an AI-powered style look transfer and quality control pipeline designed for VFX and editorial production. It transforms a new shot brief and a hero reference image into multiple style
This guide will walk you through setting up your n8n workflow. By the end, you'll have a fully automated system for managing your contract employee placements, from generating documents to sending ren
⚠️ COMMUNITY TEMPLATE DISCLAIMER: This is a community-contributed template that uses ScrapeGraphAI (a community node). Please ensure you have the ScrapeGraphAI community node installed in your n8n ins