{ "nodes": [ { "id": "8e0a3745-348b-42db-82cc-55676c897ad7", "name": "Start thread", "type": "n8n-nodes-base.slack", "position": [ 1260, 180 ], "parameters": { "text": "=๐Ÿงต Got request to `{{ $json.command }}` from @{{$json.user}}", "select": "channel", "channelId": { "__rl": true, "mode": "name", "value": "={{ $json.alerts_channel }}" }, "otherOptions": { "link_names": true } }, "credentials": { "slackApi": { "id": "26", "name": "Cloudbot bot token" } }, "typeVersion": 2 }, { "id": "ee413d6c-dad3-4e57-b08d-ffd0f84c682e", "name": "send help", "type": "n8n-nodes-base.httpRequest", "position": [ 880, 560 ], "parameters": { "url": "={{ $json.response_url }}", "options": {}, "requestMethod": "POST", "jsonParameters": true, "responseFormat": "string", "bodyParametersJson": "={\n\"attachments\": [\n{\n\"text\": \"โ„น๏ธ <{{ $json.help_docs_url }}|You can find help page here>\"\n}\n]\n}" }, "typeVersion": 1 }, { "id": "47c146f9-1223-46a7-bfd6-0fa6ff701efe", "name": "Validate Slack token", "type": "n8n-nodes-base.if", "position": [ 320, 280 ], "parameters": { "conditions": { "string": [ { "value1": "={{ $json.slack_token }}", "value2": "={{ $json.request_token }}" } ] } }, "typeVersion": 1 }, { "id": "7733505c-d02c-4cb2-be78-f2272e5b7d6e", "name": "Sticky Note3", "type": "n8n-nodes-base.stickyNote", "position": [ -440, -140 ], "parameters": { "color": 5, "width": 549.1826144791862, "height": 326.46772464213774, "content": "## ๐Ÿ‘จโ€๐ŸŽค Setup\n1. Add Slack command and point it up to the webhook\n2. Add the following to the **Set config** node\n- `alerts_channel` with alerts channel to start threads on\n- `instance_url` with this instance url to make it easy to debug\n- `slack_token` with slack bot token to validate request\n- `slack_secret_signature` with slack secret signature to validate request\n- `help_docs_url` with help url to help users understand the commands \n3. Build other workflows to call and add them to `commands` in **Set Config**. Each command must be mapped to a workflow id with an `Execute Workflow Trigger` node\n4. Activate workflow ๐Ÿš€" }, "typeVersion": 1 }, { "id": "30355072-5d75-4deb-af67-909ba59e6eb3", "name": "Reply to user that command was received", "type": "n8n-nodes-base.httpRequest", "onError": "continueRegularOutput", "position": [ 500, 40 ], "parameters": { "url": "={{ $json.response_url }}", "options": {}, "requestMethod": "POST", "jsonParameters": true, "responseFormat": "string", "bodyParametersJson": "={\n\"attachments\": [\n{\n\"text\": \"โ„น๏ธ Got command `{{ $json.command_name }} {{ $json.command_text }}`\"\n}\n]\n}" }, "typeVersion": 1 }, { "id": "a2217c45-700e-4923-96e4-455a733bc1e4", "name": "if has workflow", "type": "n8n-nodes-base.if", "position": [ 740, 280 ], "parameters": { "options": {}, "conditions": { "options": { "leftValue": "", "caseSensitive": true, "typeValidation": "strict" }, "combinator": "and", "conditions": [ { "id": "d0a35e4f-3141-4e94-bb1a-fe7747a58dfc", "operator": { "type": "object", "operation": "notEmpty", "singleValue": true }, "leftValue": "={{ $json.workflow }}", "rightValue": "" } ] } }, "typeVersion": 2 }, { "id": "7ff12aa4-680f-42af-aa2f-c9dd6a733976", "name": "Set config", "type": "n8n-nodes-base.set", "position": [ -100, 280 ], "parameters": { "options": {}, "assignments": { "assignments": [ { "id": "ba8fd958-188a-4e27-bdf1-928de8ae7d4f", "name": "commands", "type": "object", "value": "={{\n{\n \"info\": { workflowId: 142, startThread: false },\n \"delete-user\": { workflowId: \"pTh9HMZVYcQNXypJ\" }\n}\n}}" }, { "id": "105d2881-72b7-4547-a076-83ddb0966256", "name": "alerts_channel", "type": "string", "value": "#adore_bot_test" }, { "id": "9672bea2-3a6a-4162-9966-107bf2ddbee5", "name": "instance_url", "type": "string", "value": "https://x.app.n8n.cloud/" }, { "id": "52b53b37-5f69-4fb8-9569-f62788d91af1", "name": "slack_token", "type": "string", "value": "FILL_TOKEN_HERE" }, { "id": "4d8d06f2-f1a5-4eb2-a559-42d98ceddffb", "name": "slack_secret_signature", "type": "string", "value": "FILL_SECRET_HERE" }, { "id": "c2c7de20-a264-495e-934e-dda1a0bc64b9", "name": "help_docs_url", "type": "string", "value": "ADD_LINK_HERE" } ] }, "includeOtherFields": true }, "typeVersion": 3.3 }, { "id": "4c730be9-d3f5-45ee-8f2b-b6bfd685ea78", "name": "Send debug url", "type": "n8n-nodes-base.httpRequest", "onError": "continueRegularOutput", "position": [ 1260, 440 ], "parameters": { "url": "={{ $json.response_url }}", "options": {}, "requestMethod": "POST", "jsonParameters": true, "responseFormat": "string", "bodyParametersJson": "={\n\"attachments\": [\n{\n\"text\": \"<{{ $json.instance_url }}/workflow/{{ $workflow.id }}/executions/{{ $execution.id }}|To debug entry point execution>\"\n}\n]\n}" }, "retryOnFail": false, "typeVersion": 2 }, { "id": "f4ccc237-d703-4963-8112-cc38ae9d6b2a", "name": "if create thread", "type": "n8n-nodes-base.if", "position": [ 980, 280 ], "parameters": { "options": {}, "conditions": { "options": { "leftValue": "", "caseSensitive": true, "typeValidation": "strict" }, "combinator": "or", "conditions": [ { "id": "7eadbf0d-f8ec-45cf-abf3-aafb8d7e16b4", "operator": { "type": "boolean", "operation": "true", "singleValue": true }, "leftValue": "={{ $json.workflow.startThread }}", "rightValue": "" }, { "id": "2f28e7dd-6473-4f85-a449-674e00b29b4d", "operator": { "type": "boolean", "operation": "notExists", "singleValue": true }, "leftValue": "={{ $json.workflow.startThread }}", "rightValue": "" } ] } }, "typeVersion": 2 }, { "id": "ed9f2ed8-5266-42a3-9d47-621050e5bf97", "name": "Alert user that thread was created", "type": "n8n-nodes-base.httpRequest", "onError": "continueRegularOutput", "position": [ 1260, 0 ], "parameters": { "url": "={{ $json.response_url }}", "options": {}, "requestMethod": "POST", "jsonParameters": true, "responseFormat": "string", "bodyParametersJson": "={\n\"attachments\": [\n{\n\"text\": \"๐Ÿงต Thread created on {{ $json.alerts_channel }}\"\n}\n]\n}" }, "retryOnFail": false, "typeVersion": 2 }, { "id": "9904180a-e937-43fd-9b04-627e860d693a", "name": "Add debug info", "type": "n8n-nodes-base.slack", "position": [ 1540, 60 ], "parameters": { "text": "=<{{ $vars.instance_url }}/workflow/{{ $workflow.id }}/executions/{{ $execution.id }}|To debug entry point execution>", "select": "channel", "channelId": { "__rl": true, "mode": "id", "value": "={{ $json.channel }}" }, "otherOptions": { "thread_ts": { "replyValues": { "thread_ts": "={{ $json.message.ts }}" } } } }, "credentials": { "slackApi": { "id": "26", "name": "Cloudbot bot token" } }, "typeVersion": 2 }, { "id": "6b385f75-4ebf-46c8-a799-babdb6231f4e", "name": "Execute target workflow", "type": "n8n-nodes-base.executeWorkflow", "position": [ 1940, 500 ], "parameters": { "options": {}, "workflowId": "={{ $json.commands.info.workflowId }}" }, "typeVersion": 1 }, { "id": "5fde8d57-6ef3-4b01-9422-16fd2f176c5d", "name": "Add thread info", "type": "n8n-nodes-base.merge", "position": [ 1760, 320 ], "parameters": { "mode": "combine", "options": {}, "combinationMode": "multiplex" }, "typeVersion": 2 }, { "id": "c4892e34-53af-4d95-a3b6-ca16fdef1aa7", "name": "Handle other commands", "type": "n8n-nodes-base.switch", "position": [ 640, 620 ], "parameters": { "rules": { "values": [ { "outputKey": "help", "conditions": { "options": { "leftValue": "", "caseSensitive": true, "typeValidation": "strict" }, "combinator": "and", "conditions": [ { "operator": { "type": "string", "operation": "equals" }, "leftValue": "={{ $json.command }}", "rightValue": "help" } ] }, "renameOutput": true } ] }, "options": { "fallbackOutput": "extra" } }, "typeVersion": 3 }, { "id": "7dabe06a-8d87-4e68-b8d9-53bf7f29a9ab", "name": "Set thread info", "type": "n8n-nodes-base.set", "position": [ 1540, 240 ], "parameters": { "values": { "string": [ { "name": "channel_id", "value": "={{ $json.channel }}" }, { "name": "thread_ts", "value": "={{ $json.message.ts }}" } ] }, "options": {}, "keepOnlySet": true }, "typeVersion": 1 }, { "id": "e56875c4-ce2b-4639-aabc-21f1562a3858", "name": "Unknown command", "type": "n8n-nodes-base.httpRequest", "position": [ 880, 740 ], "parameters": { "url": "={{ $json.response_url }}", "options": {}, "requestMethod": "POST", "jsonParameters": true, "responseFormat": "string", "bodyParametersJson": "={\n\"attachments\": [\n{\n\"text\": \"๐Ÿคท๐Ÿฝโ€โ™‚๏ธ Sorry, unknown command `{{ $json.command }}`\"\n}\n]\n}" }, "typeVersion": 1 }, { "id": "3fab88ce-4a80-483b-b558-12e111f16c98", "name": "Set vars", "type": "n8n-nodes-base.set", "position": [ -280, 280 ], "parameters": { "options": {}, "assignments": { "assignments": [ { "id": "8fa0d712-1076-49b7-82da-e98390182ac6", "name": "command_text", "type": "string", "value": "={{ $json.body.text }}" }, { "id": "ef82aa1f-2882-4970-b10a-86e7faef6562", "name": "user", "type": "string", "value": "={{ $json.body.user_name }}" }, { "id": "633fe37e-850c-4e95-8728-f19ceb4afe76", "name": "response_url", "type": "string", "value": "={{ $json.body.response_url }}" }, { "id": "bbab2bb9-3e90-41c4-b5be-8c7873c61707", "name": "request_token", "type": "string", "value": "={{ $json.body.token }}" }, { "id": "3e6dd0e2-fec4-48cb-a44c-1342a8eb619c", "name": "command_name", "type": "string", "value": "={{ $json.body.command }}" } ] } }, "typeVersion": 3.3 }, { "id": "99cab485-4099-4505-9c9e-33ea389818e5", "name": "Webhook to call for Slack command", "type": "n8n-nodes-base.webhook", "position": [ -480, 280 ], "webhookId": "a14585bb-b757-410e-a5b2-5f05a087b388", "parameters": { "path": "a14585bb-b757-410e-a5b2-5f05a087b388", "options": { "rawBody": true, "responseData": "Wait for it...", "binaryPropertyName": "data" }, "httpMethod": "POST" }, "typeVersion": 1.1 }, { "id": "09dc7ecf-a577-427e-a193-ed29d260c5fe", "name": "Reply to user directly", "type": "n8n-nodes-base.httpRequest", "position": [ 1460, 900 ], "parameters": { "url": "={{ $json.response_url }}", "options": {}, "requestMethod": "POST", "jsonParameters": true, "responseFormat": "string", "bodyParametersJson": "={\n\"attachments\": [\n{\n\"text\": \"<{{ $json.instance_url }}workflow/{{ $workflow.id }}/executions/{{ $execution.id }}|To debug subworkflow execution>\"\n}\n]\n}" }, "typeVersion": 2, "continueOnFail": true }, { "id": "a38b3343-8e8e-4d6c-95ef-66efafdfa913", "name": "Sticky Note", "type": "n8n-nodes-base.stickyNote", "position": [ 1160, 660 ], "parameters": { "width": 1255.4495374151727, "height": 655.2393233866135, "content": "## Example subworkflow for command WITHOUT Slack thread..\n\n### Build this in a separate workflow\n### and add the id to `commands` in **Set Config**" }, "typeVersion": 1 }, { "id": "87f764b3-135c-4dc3-8633-b58e2c3a4e2d", "name": "Command workflow trigger", "type": "n8n-nodes-base.executeWorkflowTrigger", "disabled": true, "position": [ 1220, 1020 ], "parameters": {}, "typeVersion": 1 }, { "id": "3a52d7e3-ef56-47db-844a-1efb6c20ad35", "name": "if has flag", "type": "n8n-nodes-base.if", "position": [ 1400, 1120 ], "parameters": { "options": {}, "conditions": { "options": { "leftValue": "", "caseSensitive": true, "typeValidation": "strict" }, "combinator": "and", "conditions": [ { "id": "d8478e87-6e7c-40ea-a28d-099a3896001b", "operator": { "type": "array", "operation": "contains", "rightType": "any" }, "leftValue": "={{ $json.flags }}", "rightValue": "--full-info" } ] } }, "typeVersion": 2 }, { "id": "78718555-e266-4f58-ab9d-6e78f50afac2", "name": "If matches env variable", "type": "n8n-nodes-base.if", "position": [ 1620, 1120 ], "parameters": { "options": {}, "conditions": { "options": { "leftValue": "", "caseSensitive": true, "typeValidation": "strict" }, "combinator": "and", "conditions": [ { "id": "1ccb9f5d-0e7d-44f9-86e3-d5c0e15cb648", "operator": { "name": "filter.operator.equals", "type": "string", "operation": "equals" }, "leftValue": "={{ $json.env.env }}", "rightValue": "prod" } ] } }, "typeVersion": 2 }, { "id": "0ca15a51-2e56-4ef4-8be6-96f45ed17867", "name": "Found user", "type": "n8n-nodes-base.httpRequest", "position": [ 2220, 1120 ], "parameters": { "url": "={{ $('Command workflow trigger').item.json.response_url }}", "options": {}, "requestMethod": "POST", "jsonParameters": true, "responseFormat": "string", "bodyParametersJson": "={{ $json.slack_message }}" }, "typeVersion": 2, "continueOnFail": true }, { "id": "ad83305f-9ca5-428f-a731-9afe3a82258a", "name": "Format data into nice structure", "type": "n8n-nodes-base.code", "position": [ 2040, 1120 ], "parameters": { "jsCode": "const user = {\n id: '1',\n email: 'mutasem@n8n.io',\n name: 'Mutasem Aldmour',\n username: 'mutasem',\n profile_url: 'https://n8n.io/creators/mutasem/',\n}\n\nconst fields = [\n `*id:*\\n${user.id}`,\n `*email:*\\n${user.email}`,\n `*name:*\\n${user.name}`,\n `*urls:*\\n<${user.profile_url}|creator profile>`\n];\n\n// remember no more than 10 fields per section\nconst output = {\n \"blocks\":\n [\n {\n \"type\": \"section\",\n \"text\":\n {\n \"type\": \"mrkdwn\",\n \"text\": `User: *${user.username}*`\n }\n },\n {\n \"type\": \"section\",\n \"fields\": fields.map((text) => {\n return {\n \"type\": \"mrkdwn\",\n text,\n };\n })\n }\n ]\n}\n\nreturn { slack_message: output };" }, "typeVersion": 1 }, { "id": "6bdbd120-68ac-46ad-bd34-c43d7a447be4", "name": "REPLACE ME WITH TRIGGER", "type": "n8n-nodes-base.set", "position": [ 1240, 1680 ], "parameters": { "options": {} }, "typeVersion": 3.3 }, { "id": "e2b0b88d-be4a-4b66-be15-3e8c6052d0f7", "name": "Delete user here for example", "type": "n8n-nodes-base.postgres", "disabled": true, "position": [ 1500, 1800 ], "parameters": { "table": { "__rl": true, "mode": "name", "value": "=user" }, "where": { "values": [ { "value": "={{ $json.params[0] }}", "column": "username" } ] }, "schema": { "__rl": true, "mode": "list", "value": "public" }, "options": {}, "operation": "deleteTable", "deleteCommand": "delete" }, "typeVersion": 2.3 }, { "id": "b0dc9a07-4957-4643-972b-49952d6fc001", "name": "Get user here for example", "type": "n8n-nodes-base.postgres", "disabled": true, "position": [ 1840, 1120 ], "parameters": { "table": { "__rl": true, "mode": "name", "value": "test" }, "where": { "values": [ { "value": "={{ $json.params[0] }}", "column": "username" } ] }, "schema": { "__rl": true, "mode": "list", "value": "public" }, "options": {}, "operation": "select" }, "typeVersion": 2.3 }, { "id": "1f2eff56-a89b-4d6d-af8b-477c81c8bab3", "name": "Confirm user was deleted", "type": "n8n-nodes-base.slack", "position": [ 1720, 1800 ], "parameters": { "text": "Deleted user โœ…", "select": "channel", "channelId": { "__rl": true, "mode": "id", "value": "={{ $('Command workflow trigger').item.json.channel_id }}" }, "otherOptions": { "thread_ts": { "replyValues": { "thread_ts": "={{ $('Command workflow trigger').item.json.thread_ts }}" } } } }, "credentials": { "slackApi": { "id": "26", "name": "Cloudbot bot token" } }, "typeVersion": 2 }, { "id": "0c0d0487-a594-4e88-b777-21b4816115cd", "name": "Replying to thread", "type": "n8n-nodes-base.slack", "position": [ 1500, 1580 ], "parameters": { "text": "=<{{ $json.instance_url }}workflow/{{ $workflow.id }}/executions/{{ $execution.id }}|To debug subworkflow execution>", "select": "channel", "channelId": { "__rl": true, "mode": "id", "value": "={{ $json.channel_id }}" }, "otherOptions": { "thread_ts": { "replyValues": { "thread_ts": "={{ $json.thread_ts }}" } } } }, "credentials": { "slackApi": { "id": "26", "name": "Cloudbot bot token" } }, "typeVersion": 2 }, { "id": "2c055ef4-4c0a-475d-b521-30002a45950b", "name": "Sticky Note2", "type": "n8n-nodes-base.stickyNote", "position": [ 1160, 1380 ], "parameters": { "width": 961.7738517807816, "height": 589.0078772779973, "content": "## Example subworkflow for command WITH Slack thread..\n\n### Build this in a second separate workflow\n### and add the id to `commands` in **Set Config**\n\nUsed Edit Fields node here as trigger because you can only have one\nExecute Workflow Trigger per workflow" }, "typeVersion": 1 }, { "id": "aec1b842-1219-4367-9238-3c7a118ce68f", "name": "Sticky Note1", "type": "n8n-nodes-base.stickyNote", "position": [ -80, 460 ], "parameters": { "color": 7, "width": 150, "height": 83.26656725254155, "content": "### ๐Ÿ‘†๐Ÿฝ Set all custom config here " }, "typeVersion": 1 }, { "id": "dd8f1a00-dfd4-4966-a76c-3c8e2a243bab", "name": "parse command", "type": "n8n-nodes-base.code", "position": [ 560, 280 ], "parameters": { "jsCode": "const text = $input.first().json.command_text;\nconst parts = text.split(' ');\n\n\n// GET COMMAND\n// for example /cloudbot info mutasem\n// should return \"info\"\nconst command = parts[0];\n\n\n// GET FLAGS \n// for example /cloudbot info mutasem --test --flag\n// should return ['--test', '--flag']\nconst flags = parts.filter((part) => part.startsWith('--'));\n\n\n// GET PARAMS\n// for example /cloudbot info mutasem test\n// should return [\"mutasem\", \"test\"]\nlet params = parts\n .filter((part, i) => i > 0 && !part.startsWith('--'));\nparams = params.filter((param, i) => {\n if (param === '-e') {\n return false;\n }\n if (params[i - 1] === '-e') {\n return false;\n }\n\n return true;\n});\n\n\n// GET ENV VARS\n// for example /cloudbot info mutasem -e env=prod\n// should return {env: \"prod\"}\nconst env = parts.filter((val, i) => {\n return i > 0 && parts[i - 1] === '-e';\n})\n .reduce((accu, opt) => {\n if (!opt.includes('=')) {\n return accu;\n }\n\n const key = opt.split('=')[0];\n const val = opt.split('=')[1];\n \n accu[key] = clean(val);\n return accu;\n}, {});\n\n// Add workflow to run\nconst commands = $input.first().json.commands;\nlet workflow;\nif (commands[command]) {\n workflow = commands[command];\n}\n\nreturn {\n ...$input.first().json,\n command,\n flags,\n env,\n params,\n workflow,\n}\n\nfunction clean(str) {\n return str.replaceAll(`โ€˜`, '\\'').replaceAll('โ€œ', '\"').replaceAll('โ€', '\"').replaceAll('โ€™', '\\'');\n}" }, "typeVersion": 1 }, { "id": "22b8502c-dec3-4456-9947-639761517881", "name": "Validate webhook signature", "type": "n8n-nodes-base.code", "position": [ 100, 280 ], "parameters": { "jsCode": "const SIGNING_SECRET = $input.first().json.slack_secret_signature;\nconst item = $('Webhook to call for Slack command').first();\n\nif (!item.binary) {\n throw new Error('Missing binary data');\n}\n\nconst crypto = require('crypto');\nconst { binary: { data } } = item;\n\nif (\n !item.json.headers['x-slack-request-timestamp'] ||\n Math.abs(\n Math.floor(new Date().getTime() / 1000) -\n +item.json.headers['x-slack-request-timestamp']\n ) > 300\n) {\n throw new Error('Unauthorized, request not fresh');\n}\n\nconst rawBody = Buffer.from(data.data, 'base64').toString()\n\n// compute the basestring\nconst baseStr = `v0:${item.json.headers['x-slack-request-timestamp']}:${rawBody}`;\n\n// extract the received signature from the request headers\nconst receivedSignature = item.json.headers['x-slack-signature'];\n\nconst expectedSignature = `v0=${crypto.createHmac('sha256', SIGNING_SECRET)\n.update(baseStr, 'utf8')\n.digest('hex')}`;\n\n// match the two signatures\nif (expectedSignature !== receivedSignature) {\nthrow new Error('Unauthorized, umatched signatures');\n}\n\nreturn $input.all();" }, "typeVersion": 2 } ], "pinData": {}, "connections": { "Set vars": { "main": [ [ { "node": "Set config", "type": "main", "index": 0 } ] ] }, "Set config": { "main": [ [ { "node": "Validate webhook signature", "type": "main", "index": 0 } ] ] }, "if has flag": { "main": [ [ { "node": "If matches env variable", "type": "main", "index": 0 } ] ] }, "Start thread": { "main": [ [ { "node": "Add debug info", "type": "main", "index": 0 }, { "node": "Set thread info", "type": "main", "index": 0 } ] ] }, "parse command": { "main": [ [ { "node": "if has workflow", "type": "main", "index": 0 } ] ] }, "Add thread info": { "main": [ [ { "node": "Execute target workflow", "type": "main", "index": 0 } ] ] }, "Set thread info": { "main": [ [ { "node": "Add thread info", "type": "main", "index": 0 } ] ] }, "if has workflow": { "main": [ [ { "node": "if create thread", "type": "main", "index": 0 } ], [ { "node": "Handle other commands", "type": "main", "index": 0 } ] ] }, "if create thread": { "main": [ [ { "node": "Start thread", "type": "main", "index": 0 }, { "node": "Alert user that thread was created", "type": "main", "index": 0 }, { "node": "Add thread info", "type": "main", "index": 1 } ], [ { "node": "Send debug url", "type": "main", "index": 0 }, { "node": "Execute target workflow", "type": "main", "index": 0 } ] ] }, "Validate Slack token": { "main": [ [ { "node": "Reply to user that command was received", "type": "main", "index": 0 }, { "node": "parse command", "type": "main", "index": 0 } ] ] }, "Handle other commands": { "main": [ [ { "node": "send help", "type": "main", "index": 0 } ], [ { "node": "Unknown command", "type": "main", "index": 0 } ] ] }, "If matches env variable": { "main": [ [ { "node": "Get user here for example", "type": "main", "index": 0 } ] ] }, "REPLACE ME WITH TRIGGER": { "main": [ [ { "node": "Replying to thread", "type": "main", "index": 0 }, { "node": "Delete user here for example", "type": "main", "index": 0 } ] ] }, "Command workflow trigger": { "main": [ [ { "node": "Reply to user directly", "type": "main", "index": 0 }, { "node": "if has flag", "type": "main", "index": 0 } ] ] }, "Get user here for example": { "main": [ [ { "node": "Format data into nice structure", "type": "main", "index": 0 } ] ] }, "Validate webhook signature": { "main": [ [ { "node": "Validate Slack token", "type": "main", "index": 0 } ] ] }, "Delete user here for example": { "main": [ [ { "node": "Confirm user was deleted", "type": "main", "index": 0 } ] ] }, "Format data into nice structure": { "main": [ [ { "node": "Found user", "type": "main", "index": 0 } ] ] }, "Webhook to call for Slack command": { "main": [ [ { "node": "Set vars", "type": "main", "index": 0 } ] ] } } }