This workflow corresponds to n8n.io template #12062 — we link there as the canonical source.
This workflow follows the Agent → HTTP Request 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": "JxGoWSeBkNUoBvTc",
"meta": {
"templateId": "12062",
"templateCredsSetupCompleted": true
},
"name": "Control AI agent tool access with Port RBAC and Slack mentions",
"tags": [],
"nodes": [
{
"id": "5c4ceb24-7ccd-4b90-b2d7-14d723d08c74",
"name": "OpenAI Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
2448,
896
],
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "gpt-4o",
"cachedResultName": "gpt-4o"
},
"options": {}
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.2
},
{
"id": "132461b5-bd6f-4e68-b623-eaea4c536e3c",
"name": "Check permissions",
"type": "@n8n/n8n-nodes-langchain.code",
"notes": "A tool to check user's allowed tools and permissions",
"position": [
2880,
880
],
"parameters": {
"code": {
"supplyData": {
"code": "const { DynamicTool } = require(\"@langchain/core/tools\");\nconst connectedTools = await this.getInputConnectionData('ai_tool', 0);\nconst allowedTools = $input.item.json.allowed_tools;\n\nconst noTool = (tool) => {\n return new DynamicTool({\n name: tool.getName(),\n description: tool.description,\n func: async () => {\n return \"Tell the user 'You are not authorized to use this tool'.\";\n },\n });\n}\n\nreturn connectedTools.map(connectedTool => {\n const permissionGranted = allowedTools.includes(connectedTool.getName());\n return permissionGranted ? connectedTool : noTool(connectedTool);\n});"
}
},
"inputs": {
"input": [
{
"type": "ai_tool",
"required": true
}
]
},
"outputs": {
"output": [
{
"type": "ai_tool"
}
]
}
},
"typeVersion": 1
},
{
"id": "0a041ea8-f282-4120-8c4e-5e86df4373d2",
"name": "Set input",
"type": "n8n-nodes-base.set",
"position": [
2576,
624
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "9ea62c8f-984b-4c05-8e40-549d8035c4d3",
"name": "name",
"type": "string",
"value": "={{ $json.entity.identifier }}"
},
{
"id": "bf74b2c4-f0d1-458a-9044-5cb1b62722e6",
"name": "granted_roles",
"type": "array",
"value": "={{ $json.entity.relations.roles || [] }}"
},
{
"id": "e0f4d3d7-a916-43cb-a13d-e4453b0d1a3b",
"name": "allowed_tools",
"type": "array",
"value": "={{ $json.entity.properties.allowed_tools || [] }}"
},
{
"id": "26d0f442-4cd3-4930-a80b-fc471226dd36",
"name": "regions",
"type": "array",
"value": "={{ $('Get Regions from Port').item.json.entities.map(e => e.identifier) }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "5e15aaad-cf19-401b-a96a-97b9d028d177",
"name": "calculator",
"type": "@n8n/n8n-nodes-langchain.toolCalculator",
"position": [
2928,
1136
],
"parameters": {},
"typeVersion": 1
},
{
"id": "a5c4e743-3894-45c7-a3b9-fbf42ac3eed1",
"name": "Unknown user",
"type": "n8n-nodes-base.if",
"position": [
2224,
496
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "1d042f5b-ef39-4b9e-8d9c-900b39dbe3fb",
"operator": {
"type": "boolean",
"operation": "false",
"singleValue": true
},
"leftValue": "={{ $json.ok }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "966a41d6-68b0-4629-84f3-0d925b6e88e3",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
2800,
832
],
"parameters": {
"color": 7,
"width": 380,
"height": 220,
"content": "Uses list of allowed tools gathered from Port to check for permissions and replaces denied tools with a fixed instruction to return a message to the user."
},
"typeVersion": 1
},
{
"id": "9394b13a-262f-49fa-9b3e-99f1a17766a3",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
2736,
560
],
"parameters": {
"color": 7,
"width": 380,
"height": 240,
"content": "AI agent with the instruction to always use the connected tools to respond to the user's request"
},
"typeVersion": 1
},
{
"id": "b9cf7ecf-5372-4e75-be15-eebbd79a458f",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
2512,
560
],
"parameters": {
"color": 7,
"width": 220,
"height": 240,
"content": "Collects input and formats it using required keys"
},
"typeVersion": 1
},
{
"id": "f307100d-d57d-42a8-bc1f-d93a6cc62a82",
"name": "Sticky Note11",
"type": "n8n-nodes-base.stickyNote",
"position": [
1120,
416
],
"parameters": {
"color": 7,
"width": 220,
"height": 240,
"content": "Listens to messages directly sent to the Slack bot"
},
"typeVersion": 1
},
{
"id": "1be0d592-609b-45b6-9970-ad7d49f43e96",
"name": "Sticky Note12",
"type": "n8n-nodes-base.stickyNote",
"position": [
1952,
432
],
"parameters": {
"color": 7,
"width": 428,
"height": 240,
"content": "Checks if the user was found in Port"
},
"typeVersion": 1
},
{
"id": "e1f41863-12e5-4d35-8d6c-2f14884fb4ad",
"name": "Slack Trigger",
"type": "n8n-nodes-base.slackTrigger",
"position": [
1168,
496
],
"parameters": {
"options": {},
"trigger": [
"app_mention"
],
"channelId": {
"__rl": true,
"mode": "id",
"value": "channelId"
}
},
"credentials": {
"slackApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "0204fb6e-05b1-46c4-8d09-d7b8a8666b01",
"name": "Send a message",
"type": "n8n-nodes-base.slack",
"position": [
2576,
384
],
"parameters": {
"text": "User not found in Port. Please contact your administrator.",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "id",
"value": "={{ $('Slack Trigger').item.json.channel }}"
},
"otherOptions": {}
},
"credentials": {
"slackApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.3
},
{
"id": "2b119903-8698-4d05-b6ed-d50ed3f40fd0",
"name": "Get user permission from Port",
"type": "n8n-nodes-base.httpRequest",
"position": [
2016,
496
],
"parameters": {
"url": "=https://api.port.io/v1/blueprints/_user/entities/{{ $('Get user\\'s slack profile').item.json.email }}",
"options": {},
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "={{ $('Get Port access token').item.json.tokenType }} {{ $('Get Port access token').item.json.accessToken }}"
}
]
}
},
"typeVersion": 4.3
},
{
"id": "76183718-358b-443d-af04-4b7cef4130b7",
"name": "Wikipedia",
"type": "@n8n/n8n-nodes-langchain.toolWikipedia",
"position": [
3056,
1152
],
"parameters": {},
"typeVersion": 1
},
{
"id": "4f99e983-0ca3-4e79-8e55-bf4745531bf0",
"name": "Create an incident in PagerDuty",
"type": "n8n-nodes-base.pagerDutyTool",
"position": [
3248,
1136
],
"parameters": {
"email": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Email', ``, 'string') }}",
"title": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Title', ``, 'string') }}",
"resource": "incident",
"operation": "create",
"serviceId": "YOUR_PD_SERVICE_ID",
"authentication": "apiToken",
"descriptionType": "auto",
"additionalFields": {},
"conferenceBridgeUi": {}
},
"credentials": {
"pagerDutyApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "60a35b13-fa61-4b86-91e6-3e0b849b930f",
"name": "Create a bucket in AWS S3",
"type": "n8n-nodes-base.awsS3Tool",
"position": [
2768,
1136
],
"parameters": {
"name": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('BucketName', ``, 'string') }}",
"resource": "bucket",
"additionalFields": {
"region": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Region', ``, 'string') }}"
}
},
"credentials": {
"aws": {
"name": "<your credential>"
}
},
"typeVersion": 2
},
{
"id": "2a538a0c-3539-4511-8ea9-63002a6e7c2a",
"name": "Get user's slack profile",
"type": "n8n-nodes-base.slack",
"position": [
1392,
496
],
"parameters": {
"user": {
"__rl": true,
"mode": "id",
"value": "={{ $json.user }}"
},
"resource": "user",
"operation": "getProfile"
},
"credentials": {
"slackApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.3
},
{
"id": "50b8dda9-26c5-41ac-8f47-a88f688d8b81",
"name": "Get Port access token",
"type": "n8n-nodes-base.httpRequest",
"position": [
1600,
496
],
"parameters": {
"url": "https://api.port.io/v1/auth/access_token",
"method": "POST",
"options": {},
"jsonBody": "{\n \"clientId\": \"REPLACE WITH CLIENT ID\",\n \"clientSecret\": \"REPLACE WITH CLIENT SECRET\"\n}",
"sendBody": true,
"specifyBody": "json"
},
"typeVersion": 4.3
},
{
"id": "bcad96f3-3279-465c-8378-08656dd71e88",
"name": "Chat Memory",
"type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
"position": [
2640,
896
],
"parameters": {
"sessionKey": "={{ $json.name}}",
"sessionIdType": "customKey"
},
"typeVersion": 1.3
},
{
"id": "74000745-d221-4b0c-97fa-25b1f9c1a0f8",
"name": "Send output message",
"type": "n8n-nodes-base.slack",
"position": [
3184,
624
],
"parameters": {
"text": "={{ $json.output }}",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "id",
"value": "={{ $('Slack Trigger').item.json.channel }}"
},
"otherOptions": {}
},
"credentials": {
"slackApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.3
},
{
"id": "fffead23-643d-48ef-9e17-ca894ee75967",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
368,
240
],
"parameters": {
"width": 688,
"height": 576,
"content": "## AI Agent Access Control (Port + Slack)\n\nThis workflow adds role-based access control to AI agents. Users @mention the bot in Slack, and the workflow checks their permissions in Port before letting the agent use any tools.\n\n### How it works\n1. Slack trigger picks up @mentions and gets the user's email.\n2. Authenticates with Port and looks up the user in the _user blueprint.\n3. If the user exists, reads their allowed_tools array.\n4. The LangChain code node filters tools at runtime, swapping any unauthorized tool with a \"not authorized\" stub.\n5. AI agent runs with only permitted tools, then posts the response back to Slack.\n\n### Setup\n- [ ] Connect your Slack account and set the channel ID.\n- [ ] Add your OpenAI API key.\n- [ ] Get a free Port account at port.io.\n- [ ] Create the [rbac blueprints](https://docs.port.io/guides/all/implement-rbac-for-ai-agents-with-n8n-and-port/#set-up-the-port-data-model) in Port with an allowed_tools property (string array).\n- [ ] Add user entities with their email as identifier and allowed tools listed.\n- [ ] Replace the Port client ID and secret in the \"Get Port access token\" node.\n- [ ] Connect any tool credentials you want to use (PagerDuty, AWS, etc.).\n- [ ] Invite the bot to your Slack channel."
},
"typeVersion": 1
},
{
"id": "a5396cc8-81f7-454d-9e0f-fe5bf9d87652",
"name": "AI Agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
2800,
624
],
"parameters": {
"text": "={{ $('Slack Trigger').item.json.text }}",
"options": {
"systemMessage": "=You are a personal assistant. The name of the current user is \"{{ $json.name }}\"\nYou MUST only use the provided tools to process any user input. Never use general knowledge to answer questions. If you can't use a tool, tell the user why.\n\nBelow are the list of allowed tools for this user:\n{{ $json.allowed_tools }}\n\nRegions available to use when interacting with AWS: {{ $json.regions }}",
"returnIntermediateSteps": true
},
"promptType": "define"
},
"typeVersion": 1.8
},
{
"id": "076909ce-1054-41ef-8cf7-a3d85a56c493",
"name": "Get Regions from Port",
"type": "n8n-nodes-base.httpRequest",
"position": [
1792,
496
],
"parameters": {
"url": "https://api.port.io/v1/blueprints/region/entities",
"options": {},
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "={{ $('Get Port access token').item.json.tokenType }} {{ $('Get Port access token').item.json.accessToken }}"
}
]
}
},
"typeVersion": 4.3
}
],
"active": false,
"settings": {
"availableInMCP": false,
"executionOrder": "v1"
},
"versionId": "0cd92b77-2f77-46e0-aa63-5424cc642de2",
"connections": {
"AI Agent": {
"main": [
[
{
"node": "Send output message",
"type": "main",
"index": 0
}
]
]
},
"Set input": {
"main": [
[
{
"node": "AI Agent",
"type": "main",
"index": 0
}
]
]
},
"Wikipedia": {
"ai_tool": [
[
{
"node": "Check permissions",
"type": "ai_tool",
"index": 0
}
]
]
},
"calculator": {
"ai_tool": [
[
{
"node": "Check permissions",
"type": "ai_tool",
"index": 0
}
]
]
},
"Chat Memory": {
"ai_memory": [
[
{
"node": "AI Agent",
"type": "ai_memory",
"index": 0
}
]
]
},
"Unknown user": {
"main": [
[
{
"node": "Send a message",
"type": "main",
"index": 0
}
],
[
{
"node": "Set input",
"type": "main",
"index": 0
}
]
]
},
"Slack Trigger": {
"main": [
[
{
"node": "Get user's slack profile",
"type": "main",
"index": 0
}
]
]
},
"Check permissions": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"OpenAI Chat Model": {
"ai_languageModel": [
[
{
"node": "AI Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Get Port access token": {
"main": [
[
{
"node": "Get Regions from Port",
"type": "main",
"index": 0
}
]
]
},
"Get Regions from Port": {
"main": [
[
{
"node": "Get user permission from Port",
"type": "main",
"index": 0
}
]
]
},
"Get user's slack profile": {
"main": [
[
{
"node": "Get Port access token",
"type": "main",
"index": 0
}
]
]
},
"Create a bucket in AWS S3": {
"ai_tool": [
[
{
"node": "Check permissions",
"type": "ai_tool",
"index": 0
}
]
]
},
"Get user permission from Port": {
"main": [
[
{
"node": "Unknown user",
"type": "main",
"index": 0
}
]
]
},
"Create an incident in PagerDuty": {
"ai_tool": [
[
{
"node": "Check permissions",
"type": "ai_tool",
"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.
awsopenAiApipagerDutyApislackApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow implements role-based access control for AI agent tools using Port as the single source of truth for permissions. Different users get access to different tools based on their roles, without needing a separate permission database.
Source: https://n8n.io/workflows/12062/ — 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.
Time Logging On Clockify Using Slack. Uses lmChatOpenAi, toolCalculator, toolHttpRequest, toolCode. Event-driven trigger; 16 nodes.
ClockifyBlockiaWorkflow. Uses lmChatOpenAi, toolCalculator, toolHttpRequest, toolCode. Event-driven trigger; 16 nodes.
This workflow simplifies time tracking for teams and agencies by integrating Slack with Clockify. It enables users to log, update, or delete time entries directly within Slack, leveraging an AI-powere
Who is this for? Agencies, consultants, and service providers who conduct discovery calls and need to quickly turn conversations into professional proposals.
This workflow generates comprehensive B2B leads, from a selected Business type in ANY CITY IN THE WORLD, including: Company name; Website; Email (enriched with AI Agent); Phone number; Address; Main L