This workflow corresponds to n8n.io template #17966 — we link there as the canonical source.
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 →
{
"name": "Build a multi-agent support team with shared customer memory using Amazon Bedrock AgentCore",
"nodes": [
{
"id": "sticky-overview",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-560,
0
],
"parameters": {
"width": 480,
"height": 928,
"content": "## Build a multi-agent support team with shared customer memory using Amazon Bedrock AgentCore\n\n### How it works\n\nA customer asks a question in the chat and a triage agent classifies it, then routes it to one of three specialists: analysis for anything with numbers, architecture for design guidance, and research for everything else. All four agents run on one Amazon Bedrock AgentCore harness, and each call carries an Actor ID identifying the customer, so the specialist answering now uses what another learned earlier. The answer is posted to Slack, tagged with the specialist that produced it.\n\n### Setup steps\n\n- Install the verified community node `@aws/n8n-nodes-agentcore` from Settings, Community Nodes.\n- Add an Amazon Bedrock AgentCore API credential and select it on all four agent nodes.\n- Add a Slack credential and set `slackChannel` in Set Customer Context.\n- Open the chat and ask a question. The first run takes 2 to 3 minutes while the harness is created.\n\n### Customization\n\nAgent Name is the key to the team's shared memory: change it and the team starts over. Set `customerId` from your own user or ticket ID so memory follows the person rather than the browser session."
},
"typeVersion": 1
},
{
"id": "sticky-tryit",
"name": "Sticky Note Try It",
"type": "n8n-nodes-base.stickyNote",
"position": [
-560,
928
],
"parameters": {
"color": 7,
"width": 480,
"height": 320,
"content": "### Try it\n\nAsk all three in one chat:\n\n1. `p99 was 120, 340, 95, 610, 210 ms, mean and standard deviation?`\n2. `given that, how should I structure a serverless pipeline?`\n3. `what could cause a latency regression overnight?`\n\nA different specialist each time, one shared memory."
},
"typeVersion": 1
},
{
"id": "sticky-section-0",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
0,
0
],
"parameters": {
"color": 7,
"width": 544,
"height": 352,
"content": "## Identify the customer\n\nThe chat session becomes the Actor ID, so each conversation is its own customer with its own memory."
},
"typeVersion": 1
},
{
"id": "sticky-section-1",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
576,
0
],
"parameters": {
"color": 7,
"width": 544,
"height": 352,
"content": "## Triage and classify the question\n\nCreates the shared harness on the first run, returns its ARN, and reads the category the triage agent chose."
},
"typeVersion": 1
},
{
"id": "sticky-section-2",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
1152,
0
],
"parameters": {
"color": 7,
"width": 544,
"height": 672,
"content": "## Route to one specialist\n\nThe Switch picks one. Each specialist invokes the same harness by ARN with different tools granted for that call."
},
"typeVersion": 1
},
{
"id": "sticky-section-3",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
1728,
0
],
"parameters": {
"color": 7,
"width": 544,
"height": 352,
"content": "## Format and post the reply\n\nUnwraps the answer and posts it to Slack, tagged with the specialist that produced it."
},
"typeVersion": 1
},
{
"id": "chat-trigger",
"name": "When Chat Message Received",
"type": "@n8n/n8n-nodes-langchain.chatTrigger",
"position": [
80,
176
],
"parameters": {
"mode": "hostedChat",
"public": false,
"options": {
"responseMode": "lastNode"
}
},
"typeVersion": 1.4
},
{
"id": "set-context",
"name": "Set Customer Context",
"type": "n8n-nodes-base.set",
"position": [
352,
176
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "ctx-1",
"name": "agentName",
"type": "string",
"value": "support_team"
},
{
"id": "ctx-2",
"name": "customerId",
"type": "string",
"value": "={{ $json.sessionId }}"
},
{
"id": "ctx-3",
"name": "question",
"type": "string",
"value": "={{ $json.chatInput }}"
},
{
"id": "ctx-4",
"name": "slackChannel",
"type": "string",
"value": "#support"
}
]
}
},
"typeVersion": 3.5
},
{
"id": "triage",
"name": "Triage Agent",
"type": "@aws/n8n-nodes-agentcore.agentCoreHarness",
"position": [
656,
176
],
"parameters": {
"prompt": "={{ $json.question }}",
"modelId": "global.anthropic.claude-opus-4-8",
"agentName": "={{ $json.agentName }}",
"sessionId": "={{ 'cust-' + $json.customerId }}",
"systemPrompt": "You are the triage agent for a customer support team. Classify the customer's message into exactly one category:\n\n- \"analysis\" when the message contains numbers the customer wants computed or compared.\n- \"advice\" when the message asks for architecture, design, or best-practice guidance.\n- \"research\" for anything else, including troubleshooting and open questions.\n\nReply with a single JSON object and nothing else, using exactly these keys: category, summary. The summary is one short sentence restating what the customer wants.",
"modelProvider": "bedrock",
"additionalOptions": {
"actorId": "={{ $json.customerId }}",
"timeoutSeconds": 300
},
"provisioningOptions": {
"memoryMode": "managed",
"memoryStrategies": [
"SEMANTIC",
"SUMMARIZATION",
"USER_PREFERENCE"
]
}
},
"credentials": {
"agentCoreApi": {
"name": "<your credential>"
}
},
"typeVersion": 2
},
{
"id": "parse-triage",
"name": "Read Triage Decision",
"type": "n8n-nodes-base.code",
"position": [
928,
176
],
"parameters": {
"jsCode": "// The triage agent is asked for bare JSON and reliably returns it, but a model\n// can still wrap output in a fence or add a sentence. Try every shape, and fall\n// back to research rather than dropping a customer's question.\nconst raw = String($json.response ?? '').trim();\n\nfunction extractJson(text) {\n const fenced = text.match(/```(?:json)?\\s*([\\s\\S]*?)```/);\n const candidate = fenced ? fenced[1].trim() : text;\n try {\n return JSON.parse(candidate);\n } catch {}\n const start = candidate.indexOf('{');\n const end = candidate.lastIndexOf('}');\n if (start !== -1 && end > start) {\n try {\n return JSON.parse(candidate.slice(start, end + 1));\n } catch {}\n }\n return null;\n}\n\nconst parsed = extractJson(raw) ?? {};\nconst allowed = ['analysis', 'advice', 'research'];\nconst category = allowed.includes(parsed.category) ? parsed.category : 'research';\nconst ctx = $('Set Customer Context').item.json;\n\nreturn [{\n json: {\n category,\n routedByFallback: !allowed.includes(parsed.category),\n summary: parsed.summary ?? ctx.question,\n question: ctx.question,\n customerId: ctx.customerId,\n // Every specialist reuses this ARN, so only this run provisioned a harness.\n harnessArn: $json.harnessArn,\n },\n}];"
},
"typeVersion": 2
},
{
"id": "route",
"name": "Route To Specialist",
"type": "n8n-nodes-base.switch",
"position": [
1232,
496
],
"parameters": {
"rules": {
"values": [
{
"outputKey": "analysis",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "r-analysis",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.category }}",
"rightValue": "analysis"
}
]
},
"renameOutput": true
},
{
"outputKey": "advice",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "r-advice",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.category }}",
"rightValue": "advice"
}
]
},
"renameOutput": true
},
{
"outputKey": "research",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "r-research",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.category }}",
"rightValue": "research"
}
]
},
"renameOutput": true
}
]
},
"options": {}
},
"typeVersion": 3.4
},
{
"id": "spec-analysis",
"name": "Analysis Specialist",
"type": "@aws/n8n-nodes-agentcore.agentCoreHarness",
"position": [
1504,
176
],
"parameters": {
"tools": {
"tool": [
{
"name": "code_interpreter",
"type": "agentcore_code_interpreter"
}
]
},
"prompt": "={{ $json.question }}",
"addTools": true,
"agentName": "support_team",
"sessionId": "={{ 'cust-' + $json.customerId }}",
"harnessArn": "={{ $json.harnessArn }}",
"systemPrompt": "You are the analysis specialist on a customer support team. Compute every number by writing and running Python in the code interpreter. Never estimate and never do mental arithmetic. Use what you remember about this customer where it is relevant. Answer in at most four short sentences, suitable for a Slack reply.",
"additionalOptions": {
"actorId": "={{ $json.customerId }}",
"timeoutSeconds": 300
}
},
"credentials": {
"agentCoreApi": {
"name": "<your credential>"
}
},
"typeVersion": 2
},
{
"id": "spec-advice",
"name": "Architecture Specialist",
"type": "@aws/n8n-nodes-agentcore.agentCoreHarness",
"position": [
1504,
336
],
"parameters": {
"prompt": "={{ $json.question }}",
"skills": {
"skill": [
{
"paths": "core-skills/*",
"source": "awsSkills"
}
]
},
"addSkills": true,
"agentName": "support_team",
"sessionId": "={{ 'cust-' + $json.customerId }}",
"harnessArn": "={{ $json.harnessArn }}",
"systemPrompt": "You are the architecture specialist on a customer support team. Use the loaded AWS skills for design and best-practice guidance, and use what you remember about this customer, including their scale and region, where it is relevant. Answer in at most four short sentences, suitable for a Slack reply.",
"additionalOptions": {
"actorId": "={{ $json.customerId }}",
"timeoutSeconds": 300
}
},
"credentials": {
"agentCoreApi": {
"name": "<your credential>"
}
},
"typeVersion": 2
},
{
"id": "spec-research",
"name": "Research Specialist",
"type": "@aws/n8n-nodes-agentcore.agentCoreHarness",
"position": [
1504,
496
],
"parameters": {
"prompt": "={{ $json.question }}",
"agentName": "support_team",
"sessionId": "={{ 'cust-' + $json.customerId }}",
"harnessArn": "={{ $json.harnessArn }}",
"systemPrompt": "You are the research specialist on a customer support team. Investigate the question using the shell and file tools in your sandbox where that helps, and use what you remember about this customer. If you are not certain, say what you would check next rather than guessing. Answer in at most four short sentences, suitable for a Slack reply.",
"additionalOptions": {
"actorId": "={{ $json.customerId }}",
"timeoutSeconds": 300
}
},
"credentials": {
"agentCoreApi": {
"name": "<your credential>"
}
},
"typeVersion": 2
},
{
"id": "format-reply",
"name": "Format Reply",
"type": "n8n-nodes-base.code",
"position": [
1808,
176
],
"parameters": {
"jsCode": "// One specialist ran, so exactly one branch reaches here. Unwrap fenced output\n// and never post an empty message to Slack.\nconst raw = String($json.response ?? '').trim();\n\nlet text = raw;\nconst fenced = raw.match(/^```(?:json)?\\s*([\\s\\S]*?)```$/);\nif (fenced) text = fenced[1].trim();\n\nif (!text) {\n text = 'The agent returned an empty response, which is usually a transient service error. Please ask again.';\n}\n\nconst ctx = $('Read Triage Decision').item.json;\n\nreturn [{\n json: {\n reply: text,\n answeredBy: ctx.category,\n customerId: ctx.customerId,\n usedCodeInterpreter: Array.isArray($json.toolUses)\n && $json.toolUses.some((t) => String(t?.name ?? '').includes('code_interpreter')),\n latencyMs: $json.latencyMs ?? null,\n },\n}];"
},
"typeVersion": 2
},
{
"id": "reply",
"name": "Post Answer To Slack",
"type": "n8n-nodes-base.slack",
"position": [
2080,
176
],
"parameters": {
"text": "=*Answered by the {{ $json.answeredBy }} specialist* (customer: {{ $json.customerId }})\n\n{{ $json.reply }}",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "name",
"value": "={{ $('Set Customer Context').item.json.slackChannel }}"
},
"otherOptions": {
"includeLinkToWorkflow": false
}
},
"credentials": {
"slackApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.5
}
],
"connections": {
"Format Reply": {
"main": [
[
{
"node": "Post Answer To Slack",
"type": "main",
"index": 0
}
]
]
},
"Triage Agent": {
"main": [
[
{
"node": "Read Triage Decision",
"type": "main",
"index": 0
}
]
]
},
"Analysis Specialist": {
"main": [
[
{
"node": "Format Reply",
"type": "main",
"index": 0
}
]
]
},
"Research Specialist": {
"main": [
[
{
"node": "Format Reply",
"type": "main",
"index": 0
}
]
]
},
"Route To Specialist": {
"main": [
[
{
"node": "Analysis Specialist",
"type": "main",
"index": 0
}
],
[
{
"node": "Architecture Specialist",
"type": "main",
"index": 0
}
],
[
{
"node": "Research Specialist",
"type": "main",
"index": 0
}
]
]
},
"Read Triage Decision": {
"main": [
[
{
"node": "Route To Specialist",
"type": "main",
"index": 0
}
]
]
},
"Set Customer Context": {
"main": [
[
{
"node": "Triage Agent",
"type": "main",
"index": 0
}
]
]
},
"Architecture Specialist": {
"main": [
[
{
"node": "Format Reply",
"type": "main",
"index": 0
}
]
]
},
"When Chat Message Received": {
"main": [
[
{
"node": "Set Customer Context",
"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.
agentCoreApislackApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow receives an n8n Chat message, uses Amazon Bedrock AgentCore to triage it and route it to one of three specialist agents that share customer memory, then posts the specialist’s reply to a Slack channel. Receives a new message in n8n Chat and starts a run for that…
Source: https://n8n.io/workflows/17966/ — 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.
The original LLM Council concept was introduced by Andrej Karpathy and published as an open-source repository demonstrating multi-model consensus and ranking. This workflow is my adaptation of that or
Perfect for educators, consultants, and content creators who record sessions and want to repurpose them into social media posts, videos, and images without manual work. Chat interface triggers the AI
Send an AI a few details about your "Dream Customer" in normal english, then have it search the web and give you a "Dream 100" - 100 ideal prospects to connect with in your industry.
Prospect Bot - template. Uses chatTrigger, httpRequest, slack, chat. Chat trigger; 25 nodes.
This workflow is designed for growth agencies, SaaS founders, and sales teams who want to move beyond static lead forms. It is ideal for those who need a "living" system that not only captures leads b