{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Gate AI Slack bot actions with role-based permissions from Permit.io",
  "tags": [],
  "nodes": [
    {
      "id": "e2b71c25-9820-4fc3-8edb-ac66a67598d7",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        16,
        -64
      ],
      "parameters": {
        "width": 436,
        "height": 712,
        "content": "## Gate AI Slack bot actions with role-based permissions from Permit.io\n\n### How it works\nA Slack bot that handles DevOps requests and checks every action against Permit.io RBAC before execution.\n\n1. Team member @mentions the bot with a request\n2. OpenAI classifies the intent into action + resource\n3. Permit.io checks if the user has permission\n4. Allowed \u2192 executes the action, posts result to Slack\n5. Denied \u2192 shows what the user can do and who to escalate to\n\n### Setup\n1. Install `@permitio/n8n-nodes-permitio` community node\n2. Configure Slack app with `app_mentions:read`, `chat:write`, `channels:read`, `users:read`\n3. Add OpenAI API key\n4. In Permit.io, create resources (logs, staging, production, secrets) with actions (view, deploy, restart, rotate)\n5. Create roles: viewer, developer, sre, admin\n6. Sync Slack user IDs as users in Permit.io and assign roles\n\n### Customization\n- Replace the mock HTTP Request with your real infra endpoints\n- Add ABAC conditions in Permit.io for time-based or amount-based rules without changing this workflow"
      },
      "typeVersion": 1
    },
    {
      "id": "96226226-75ad-4647-8353-c2abb4eecbab",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        432,
        96
      ],
      "parameters": {
        "color": 7,
        "width": 516,
        "height": 288,
        "content": "### 1. Trigger & Classify\nSlack trigger captures the @mention, then OpenAI extracts the action and resource from natural language."
      },
      "typeVersion": 1
    },
    {
      "id": "a83091f1-f07a-494d-834f-adb23750680a",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        896,
        80
      ],
      "parameters": {
        "color": 7,
        "width": 268,
        "height": 304,
        "content": "### 2. Permission Gate\nPermit.io checks RBAC policy. The IF node branches on the result."
      },
      "typeVersion": 1
    },
    {
      "id": "433a4b61-fb3d-461f-a18d-72a92a2e35b8",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1312,
        -64
      ],
      "parameters": {
        "color": 7,
        "width": 548,
        "height": 352,
        "content": "### 3a. Allowed \u2192 Execute\nReplace this HTTP Request with your actual infra endpoint (GitHub Actions, ArgoCD, Jenkins, etc.)"
      },
      "typeVersion": 1
    },
    {
      "id": "898d810a-fa16-406c-b08e-3d677c66ffee",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1280,
        160
      ],
      "parameters": {
        "color": 7,
        "width": 844,
        "height": 320,
        "content": "### 3b. Denied \u2192 Help\nTells the user what they CAN do and who to escalate to."
      },
      "typeVersion": 1
    },
    {
      "id": "d7fe3502-734f-4d64-8a53-44d7596dd229",
      "name": "Slack Trigger - Bot Mention",
      "type": "n8n-nodes-base.slackTrigger",
      "position": [
        528,
        224
      ],
      "parameters": {
        "options": {},
        "trigger": [
          "app_mention"
        ],
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "f7b009d2-7db5-4c38-badc-4e84d5a5b607",
      "name": "Classify DevOps Intent",
      "type": "n8n-nodes-base.openAi",
      "position": [
        752,
        224
      ],
      "parameters": {
        "model": "gpt-4o",
        "prompt": {
          "messages": [
            {
              "role": "system",
              "content": "You are an intent classifier for a DevOps Slack bot. Given a user message, extract the action and resource.  Valid actions: deploy, restart, view, rotate Valid resources: staging, production, logs, secrets  Respond ONLY with valid JSON, no markdown: {\"action\": \"<action>\", \"resource\": \"<resource>\", \"summary\": \"<one line summary of what the user wants>\"}  If the message doesn't match a DevOps action, respond with: {\"action\": \"unknown\", \"resource\": \"unknown\", \"summary\": \"<what the user asked>\"}"
            },
            {
              "content": "={{ $json.text }}"
            }
          ]
        },
        "options": {
          "temperature": 0
        },
        "resource": "chat",
        "requestOptions": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "e3f3bd66-7d50-402c-b5ab-54b70873fcc0",
      "name": "Check Permission",
      "type": "@permitio/n8n-nodes-permitio.permit",
      "position": [
        976,
        224
      ],
      "parameters": {
        "user": "={{ $('Slack Trigger - Bot Mention').item.json.user }}",
        "action": "={{ JSON.parse($json.message.content).action }}",
        "resource": "={{ JSON.parse($json.message.content).resource }}"
      },
      "credentials": {
        "permitApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "0880127b-7f77-4658-975f-50ec1fa4e169",
      "name": "Is Allowed?",
      "type": "n8n-nodes-base.if",
      "position": [
        1200,
        224
      ],
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $json.allow }}",
              "value2": true
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "3b508186-d1f2-4ed1-a801-7b29e6df9413",
      "name": "Execute DevOps Action (Mock)",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1424,
        128
      ],
      "parameters": {
        "url": "https://httpbin.org/post",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "action",
              "value": "={{ JSON.parse($('Classify DevOps Intent').item.json.message.content).action }}"
            },
            {
              "name": "resource",
              "value": "={{ JSON.parse($('Classify DevOps Intent').item.json.message.content).resource }}"
            },
            {
              "name": "triggered_by",
              "value": "={{ $('Slack Trigger - Bot Mention').item.json.user }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "f549ad19-adf1-479f-8cb7-7028b92b870d",
      "name": "Slack - Action Succeeded",
      "type": "n8n-nodes-base.slack",
      "position": [
        1648,
        128
      ],
      "parameters": {
        "text": "= Action executed successfully\n\n- Action: {{ JSON.parse($('Classify DevOps Intent').item.json.message.content).action }}\n- Resource: {{ JSON.parse($('Classify DevOps Intent').item.json.message.content).resource }}\n- Summary: {{ JSON.parse($('Classify DevOps Intent').item.json.message.content).summary }}\n- Triggered by: <@{{ $('Slack Trigger - Bot Mention').item.json.user }}>",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Slack Trigger - Bot Mention').item.json.channel }}"
        },
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "60c248fe-3748-4b7a-9ea5-a8cb95965e3d",
      "name": "Get User Permissions",
      "type": "@permitio/n8n-nodes-permitio.permit",
      "position": [
        1424,
        320
      ],
      "parameters": {
        "operation": "getUserPermissions",
        "resourceTypes": "=logs,staging,production,secrets",
        "userPermissionsUser": "={{ $('Slack Trigger - Bot Mention').item.json.user }}"
      },
      "credentials": {
        "permitApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "4b8739bf-7616-4c5c-b32f-42287a1e17c4",
      "name": "Get Authorized Users",
      "type": "@permitio/n8n-nodes-permitio.permit",
      "position": [
        1648,
        320
      ],
      "parameters": {
        "operation": "getAuthorizedUsers",
        "authorizedUsersAction": "={{ JSON.parse($('Classify DevOps Intent').item.json.message.content).action }}",
        "authorizedUsersResourceType": "={{ JSON.parse($('Classify DevOps Intent').item.json.message.content).resource }}"
      },
      "credentials": {
        "permitApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "80b9ad86-d7fe-47bc-bc42-c165b9b4fa10",
      "name": "Slack - Permission Denied",
      "type": "n8n-nodes-base.slack",
      "position": [
        1872,
        320
      ],
      "parameters": {
        "text": "=\ud83d\udeab Permission denied\n\n<@{{ $('Slack Trigger - Bot Mention').item.json.user }}>, you don't have permission to `{{ JSON.parse($('Classify DevOps Intent').item.json.message.content).action }}` on `{{ JSON.parse($('Classify DevOps Intent').item.json.message.content).resource }}`.\n\nYour current permissions:\n{{ $('Get User Permissions').item.json['__tenant:default'].permissions.join(', ') }}\n\nAuthorized users who can help:\n{{ Object.keys($('Get Authorized Users').item.json.users || {}).join(', ') }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Slack Trigger - Bot Mention').item.json.channel }}"
        },
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "connections": {
    "Is Allowed?": {
      "main": [
        [
          {
            "node": "Execute DevOps Action (Mock)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Get User Permissions",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Permission": {
      "main": [
        [
          {
            "node": "Is Allowed?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Authorized Users": {
      "main": [
        [
          {
            "node": "Slack - Permission Denied",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get User Permissions": {
      "main": [
        [
          {
            "node": "Get Authorized Users",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Classify DevOps Intent": {
      "main": [
        [
          {
            "node": "Check Permission",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Slack Trigger - Bot Mention": {
      "main": [
        [
          {
            "node": "Classify DevOps Intent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Execute DevOps Action (Mock)": {
      "main": [
        [
          {
            "node": "Slack - Action Succeeded",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}