This workflow corresponds to n8n.io template #17817 — 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": "Calculate verified campaign statistics with Amazon Bedrock AgentCore and Slack",
"nodes": [
{
"id": "37a741bd-520c-4b10-92a1-81b7daa27300",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-560,
0
],
"parameters": {
"width": 480,
"height": 912,
"content": "## Calculate verified campaign statistics with Amazon Bedrock AgentCore and Slack\n\n### How it works\n\nPost a batch of numbers to the webhook and the agent writes Python, runs it in an Amazon Bedrock AgentCore code interpreter sandbox, and returns real statistics rather than predicted ones. A Code node then recomputes every reported figure in n8n and compares, so a mismatch or a run where the tool never fired is marked unverified. The verified summary is posted to Slack.\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 the agent node.\n- Add a Slack credential and set `slackChannel`, or delete that node to run with one credential.\n- Activate the workflow, then post a batch of values to the production webhook URL.\n\n### Customization\n\nKeep Add Tools switched on: with it off the tool is ignored and the agent answers from the model alone. Edit the prompt for percentiles, correlations, or a regression, since the tool is a Python environment rather than a fixed formula."
},
"typeVersion": 1
},
{
"id": "sticky-tryit",
"name": "Sticky Note Try It",
"type": "n8n-nodes-base.stickyNote",
"position": [
-560,
960
],
"parameters": {
"color": 7,
"width": 480,
"height": 352,
"content": "### Try it\n\n```\ncurl -X POST <production-url> \\\n -H 'Content-Type: application/json' \\\n -d '{\"label\":\"Q3 email\",\n \"values\":[12,45,7,88,23,\n 56,91,34,19,67]}'\n```\n\nMean is 44.2 and the population standard deviation is 28.944084."
},
"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": "## Receive the campaign payload\n\nThe webhook accepts a POST with a values array, and Set Channel Config holds the Slack channel and agent name."
},
"typeVersion": 1
},
{
"id": "sticky-section-1",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
576,
0
],
"parameters": {
"color": 7,
"width": 816,
"height": 352,
"content": "## Compute, verify, and notify\n\nThe agent runs Python in the code interpreter, a Code node recomputes every figure to verify it, then Slack gets the result."
},
"typeVersion": 1
},
{
"id": "webhook",
"name": "When Campaign Data Received",
"type": "n8n-nodes-base.webhook",
"position": [
80,
176
],
"parameters": {
"path": "campaign-statistics",
"options": {},
"httpMethod": "POST"
},
"typeVersion": 2
},
{
"id": "config",
"name": "Set Channel Config",
"type": "n8n-nodes-base.set",
"position": [
352,
176
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "cfg-1",
"name": "agentName",
"type": "string",
"value": "campaign_statistics"
},
{
"id": "cfg-2",
"name": "slackChannel",
"type": "string",
"value": "#analytics"
},
{
"id": "cfg-3",
"name": "label",
"type": "string",
"value": "={{ $json.body?.label || 'unlabelled batch' }}"
},
{
"id": "cfg-4",
"name": "values",
"type": "array",
"value": "={{ $json.body?.values || [] }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "statistics-agent",
"name": "Campaign Analysis Agent",
"type": "@aws/n8n-nodes-agentcore.agentCoreHarness",
"position": [
656,
176
],
"parameters": {
"tools": {
"tool": [
{
"name": "code_interpreter",
"type": "agentcore_code_interpreter"
}
]
},
"prompt": "=Compute descriptive statistics for the batch labelled \"{{ $json.label }}\".\n\nValues: {{ JSON.stringify($json.values) }}",
"addTools": true,
"agentName": "={{ $json.agentName }}",
"sessionId": "={{ 'stats-' + $execution.id }}",
"systemPrompt": "You are a data analyst. You must compute every number by writing and running Python in the code interpreter. Never estimate, never do mental arithmetic, and never report a figure you did not compute in code. Use the population standard deviation (statistics.pstdev). Reply with a single JSON object and nothing else, using exactly these keys: label, count, mean, median, stdev, min, max. All numeric values must be plain numbers.",
"additionalOptions": {
"maxTokens": 4096,
"timeoutSeconds": 300
},
"provisioningOptions": {
"memoryMode": "disabled"
}
},
"credentials": {
"agentCoreApi": {
"name": "<your credential>"
}
},
"typeVersion": 2
},
{
"id": "verify",
"name": "Parse and Verify Data",
"type": "n8n-nodes-base.code",
"position": [
928,
176
],
"parameters": {
"jsCode": "// 1. Parse the agent's JSON defensively - it may arrive fenced or with prose.\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 stats = extractJson(raw);\nconst values = ($('Set Channel Config').item.json.values || []).map(Number).filter((n) => Number.isFinite(n));\n\nif (!stats || values.length === 0) {\n return [{\n json: {\n ok: false,\n verified: false,\n reason: !stats\n ? 'Could not parse a JSON object from the agent response.'\n : 'No numeric values were supplied in the webhook body.',\n rawResponse: raw.slice(0, 600),\n },\n }];\n}\n\n// 2. Recompute every figure the workflow reports, and compare. The code\n// interpreter is exact, but any prose the model writes around the tool output\n// is not, so nothing is labelled verified unless it was checked here.\nconst sorted = [...values].sort((a, b) => a - b);\nconst mid = Math.floor(sorted.length / 2);\n\nconst local = {\n count: values.length,\n mean: values.reduce((a, b) => a + b, 0) / values.length,\n median: sorted.length % 2 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2,\n min: sorted[0],\n max: sorted[sorted.length - 1],\n};\n// Population standard deviation, matching statistics.pstdev in the prompt.\nlocal.stdev = Math.sqrt(\n values.reduce((acc, v) => acc + (v - local.mean) ** 2, 0) / values.length,\n);\n\nfunction matches(reported, computed) {\n const r = Number(reported);\n if (!Number.isFinite(r)) return false;\n const tolerance = Math.max(1e-6, Math.abs(computed) * 1e-6);\n return Math.abs(r - computed) <= tolerance;\n}\n\nconst fields = ['count', 'mean', 'median', 'stdev', 'min', 'max'];\nconst fieldChecks = {};\nconst mismatched = [];\nfor (const f of fields) {\n const ok = matches(stats[f], local[f]);\n fieldChecks[f] = ok;\n if (!ok) mismatched.push(f);\n}\n\n// The code interpreter must have run. A figure produced without it was predicted\n// by the model, not computed, so it does not qualify as verified.\nconst codeInterpreterUsed = Array.isArray($json.toolUses)\n && $json.toolUses.some((t) => String(t?.name ?? '').includes('code_interpreter'));\n\nconst verified = mismatched.length === 0 && codeInterpreterUsed;\n\nlet note;\nif (verified) {\n note = 'Every reported figure matches an independent recomputation in n8n.';\n} else if (!codeInterpreterUsed) {\n note = 'The code interpreter did not run, so these figures came from the model rather than from executed code. Check that Add Tools is enabled.';\n} else {\n note = `Mismatch against the local recomputation for: ${mismatched.join(', ')}. Do not trust these figures.`;\n}\n\nreturn [{\n json: {\n ok: true,\n verified,\n codeInterpreterUsed,\n label: stats.label ?? $('Set Channel Config').item.json.label,\n count: stats.count ?? local.count,\n mean: Number(stats.mean),\n median: stats.median ?? null,\n stdev: stats.stdev ?? null,\n min: stats.min ?? null,\n max: stats.max ?? null,\n check: {\n fields: fieldChecks,\n mismatched,\n recomputed: local,\n note,\n },\n latencyMs: $json.latencyMs ?? null,\n },\n}];"
},
"typeVersion": 2
},
{
"id": "post-summary",
"name": "Send Summary to Slack",
"type": "n8n-nodes-base.slack",
"position": [
1216,
176
],
"parameters": {
"text": "=*Campaign statistics: {{ $json.label }}*\n{{ $json.verified ? ':white_check_mark: verified' : ':warning: NOT verified - see check.note' }}\n\n\u2022 count: {{ $json.count }}\n\u2022 mean: {{ $json.mean }}\n\u2022 median: {{ $json.median }}\n\u2022 std dev: {{ $json.stdev }}\n\u2022 min / max: {{ $json.min }} / {{ $json.max }}\n\nComputed in a microVM via the code interpreter.",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "name",
"value": "={{ $('Set Channel Config').item.json.slackChannel }}"
},
"otherOptions": {
"includeLinkToWorkflow": false
}
},
"credentials": {
"slackApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
}
],
"connections": {
"Set Channel Config": {
"main": [
[
{
"node": "Campaign Analysis Agent",
"type": "main",
"index": 0
}
]
]
},
"Parse and Verify Data": {
"main": [
[
{
"node": "Send Summary to Slack",
"type": "main",
"index": 0
}
]
]
},
"Campaign Analysis Agent": {
"main": [
[
{
"node": "Parse and Verify Data",
"type": "main",
"index": 0
}
]
]
},
"When Campaign Data Received": {
"main": [
[
{
"node": "Set Channel Config",
"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 a webhook payload of numeric campaign values, computes descriptive statistics using Amazon Bedrock AgentCore with the Code Interpreter tool, verifies the results in n8n, and posts a formatted summary to a Slack channel. Receives an HTTP POST request on a…
Source: https://n8n.io/workflows/17817/ — 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.
WF-OB-2: Post-Call Handler. Uses httpRequest, slack. Webhook trigger; 48 nodes.
WF-SDR-TOOLS (Agent Email + Notify). Uses httpRequest, slack. Webhook trigger; 15 nodes.
Multi-Agent Pipeline Orchestrator. Uses httpRequest, executeCommand, slack, readWriteFile. Webhook trigger; 11 nodes.
PR Review - Trigger Reviewer. Uses postgres, executeCommand, slack. Webhook trigger; 10 nodes.
AI Support Agent - Lead Routing. Uses slack, gmail, httpRequest, googleSheets. Webhook trigger; 10 nodes.