This workflow corresponds to n8n.io template #13855 — we link there as the canonical source.
This workflow follows the Agent → OpenAI Chat 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 →
{
"meta": {
"templateCredsSetupCompleted": true
},
"nodes": [
{
"id": "7a174902-d92d-4f7f-9cc1-c31bf03cdc9e",
"name": "Respond - Result",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
-160,
1264
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={{ JSON.stringify({ feedbackId: $json.feedbackId, category: $json.category, action: $json.action, destination: $json.destination, confidence: $json.confidence }) }}"
},
"typeVersion": 1.1
},
{
"id": "618c9dfe-6534-4540-8421-a61c0c0d3d13",
"name": "Queue for Human Review",
"type": "n8n-nodes-base.code",
"position": [
-384,
1648
],
"parameters": {
"jsCode": "return { json: { ...$('Validate AI Output').first().json, action: 'needs_human_review', reason: 'low_confidence_or_validation_failure' } };"
},
"typeVersion": 2
},
{
"id": "c0344566-9966-4633-9fc1-3f14715f0ac9",
"name": "Flag as Testimonial",
"type": "n8n-nodes-base.code",
"position": [
-384,
1456
],
"parameters": {
"jsCode": "return { json: { ...$('Validate AI Output').first().json, action: 'testimonial_candidate', destination: 'marketing' } };"
},
"typeVersion": 2
},
{
"id": "3d1670fb-5f75-4ca5-bd33-e2d710005e2c",
"name": "Escalate to CS",
"type": "n8n-nodes-base.code",
"position": [
-384,
1264
],
"parameters": {
"jsCode": "return { json: { ...$('Validate AI Output').first().json, action: 'escalated_to_cs', destination: 'customer_success', priority: 'high' } };"
},
"typeVersion": 2
},
{
"id": "b537f155-c8f6-4b92-ae19-15cad4ce696e",
"name": "Add to Backlog",
"type": "n8n-nodes-base.code",
"position": [
-384,
1072
],
"parameters": {
"jsCode": "return { json: { ...$('Validate AI Output').first().json, action: 'added_to_backlog', destination: 'product' } };"
},
"typeVersion": 2
},
{
"id": "4ef1f784-f94a-4479-aca2-a75cf7f5f024",
"name": "Create Jira Ticket",
"type": "n8n-nodes-base.code",
"position": [
-384,
880
],
"parameters": {
"jsCode": "return { json: { ...$('Validate AI Output').first().json, action: 'jira_ticket_created', destination: 'engineering' } };"
},
"typeVersion": 2
},
{
"id": "52463424-564d-4b05-94e0-b08cd3408a8b",
"name": "Route by Type",
"type": "n8n-nodes-base.switch",
"position": [
-608,
1136
],
"parameters": {
"rules": {
"values": [
{
"outputKey": "Product Issue",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "d6",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $('Validate AI Output').item.json.category }}",
"rightValue": "product_issue"
}
]
},
"renameOutput": true
},
{
"outputKey": "Feature Request",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "d7",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $('Validate AI Output').item.json.category }}",
"rightValue": "feature_request"
}
]
},
"renameOutput": true
},
{
"outputKey": "Complaint",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "d8",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $('Validate AI Output').item.json.category }}",
"rightValue": "complaint"
}
]
},
"renameOutput": true
},
{
"outputKey": "Praise",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "ad5fb6da-9fe3-4938-afa0-1224711fe3e4",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $('Validate AI Output').item.json.category }}",
"rightValue": "praise"
}
]
},
"renameOutput": true
}
]
},
"options": {
"fallbackOutput": 2
}
},
"typeVersion": 3.2
},
{
"id": "de3d0075-8164-49c0-9628-f5b16a6f4a30",
"name": "High Confidence + Valid?",
"type": "n8n-nodes-base.if",
"position": [
-832,
1264
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "d4",
"operator": {
"type": "number",
"operation": "gte"
},
"leftValue": "={{ $('Validate AI Output').item.json.confidence }}",
"rightValue": 0.7
},
{
"id": "d5",
"operator": {
"type": "boolean",
"operation": "true"
},
"leftValue": "={{ $('Validate AI Output').item.json.validationPassed }}",
"rightValue": true
}
]
}
},
"typeVersion": 2.2
},
{
"id": "efd5f629-be98-49c7-a329-841aca4e53fb",
"name": "AI - Classify + Draft",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
-1760,
1344
],
"parameters": {
"text": "=Analyze this customer feedback and return ONLY valid JSON.\n\nCustomer: {{ $('Has Required Fields?').item.json.customerName }}\nProduct: {{ $('Has Required Fields?').item.json.product }}\nFeedback: {{ $('Has Required Fields?').item.json.feedbackText }}\n\nReturn exactly these fields: sentiment, category, priority, suggestedResponse, confidence\n\nUse only these categories: bug_report, feature_request, praise, complaint, question",
"options": {},
"promptType": "define"
},
"typeVersion": 1.7
},
{
"id": "f43c9080-933e-4fd7-8920-f919cea636ed",
"name": "Respond - Missing Fields",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
-2048,
1904
],
"parameters": {
"options": {
"responseCode": 400
},
"respondWith": "json",
"responseBody": "={{ JSON.stringify({ status: 'error', message: 'Email and feedback text are required' }) }}"
},
"typeVersion": 1.1
},
{
"id": "c1be1b91-c98e-4c4c-bbab-5b9b6281b444",
"name": "Has Required Fields?",
"type": "n8n-nodes-base.if",
"position": [
-2336,
1760
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "req-check",
"operator": {
"type": "boolean",
"operation": "true"
},
"leftValue": "={{ $json.hasRequiredFields }}",
"rightValue": true
}
]
}
},
"typeVersion": 2.2
},
{
"id": "30628f38-dd8b-4990-b6f2-867317326951",
"name": "Normalize Feedback",
"type": "n8n-nodes-base.code",
"position": [
-2560,
1760
],
"parameters": {
"jsCode": "// Stage 1: Normalize and clean feedback data\nconst raw = $input.first().json.body;\n\nconst normalized = {\n feedbackId: 'FB-' + Date.now(),\n customerName: (raw.name || raw.fullName || 'Anonymous').trim(),\n customerEmail: (raw.email || '').toLowerCase().trim(),\n feedbackText: (raw.feedback || raw.message || raw.body || '').trim(),\n product: (raw.product || raw.service || 'general').toLowerCase().trim(),\n source: (raw.source || 'web').toLowerCase(),\n receivedAt: new Date().toISOString()\n};\n\nnormalized.hasRequiredFields = !!(normalized.customerEmail && normalized.feedbackText);\n\nreturn { json: normalized };"
},
"typeVersion": 2
},
{
"id": "a46e4d0c-7bce-4568-a516-5cec16564a44",
"name": "Webhook - Feedback Intake",
"type": "n8n-nodes-base.webhook",
"position": [
-2784,
1760
],
"parameters": {
"path": "customer-feedback",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2
},
{
"id": "6ab78c6a-8b90-4030-948a-cbcd1df3e0fd",
"name": "Input Guardrails",
"type": "@n8n/n8n-nodes-langchain.guardrails",
"position": [
-2112,
1504
],
"parameters": {
"text": "={{ $json.feedbackText }}",
"guardrails": {
"pii": {
"value": {
"type": "all"
}
},
"jailbreak": {
"value": {
"threshold": 0.8
}
},
"secretKeys": {
"value": {
"permissiveness": "balanced"
}
}
}
},
"typeVersion": 1
},
{
"id": "ed87ab69-58d9-41df-844b-e8730ba2cdae",
"name": "Input Guardrails LLM",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
-2048,
1728
],
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "gpt-4o",
"cachedResultName": "gpt-4o"
},
"options": {}
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.2
},
{
"id": "1e2c8e68-1c4e-4f08-96b5-b6788bf5c340",
"name": "Validate AI Output",
"type": "n8n-nodes-base.code",
"position": [
-1408,
1456
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const input = $json;\nlet parsed;\ntry {\n let raw = typeof input.output === 'string' ? input.output : JSON.stringify(input.output);\n raw = raw.replace(/```json\\n?/g, '').replace(/```\\n?/g, '').trim();\n parsed = JSON.parse(raw);\n} catch (e) {\n return { json: { ...input, validationPassed: false, validationError: 'Invalid JSON in AI output' } };\n}\nconst required = ['sentiment', 'category', 'priority', 'suggestedResponse'];\nconst missing = required.filter(f => !parsed[f]);\nif (missing.length > 0) {\n return { json: { ...input, ...parsed, validationPassed: false, validationError: 'Missing fields: ' + missing.join(', ') } };\n}\nconst validSentiments = ['positive', 'negative', 'neutral', 'mixed'];\nconst validCategories = ['bug_report', 'feature_request', 'praise', 'complaint', 'question'];\nconst validPriorities = ['critical', 'high', 'medium', 'low'];\nconst errors = [];\nif (!validSentiments.includes(parsed.sentiment)) errors.push('Invalid sentiment');\nif (!validCategories.includes(parsed.category)) errors.push('Invalid category');\nif (!validPriorities.includes(parsed.priority)) errors.push('Invalid priority');\nif (errors.length > 0) {\n return { json: { ...input, ...parsed, validationPassed: false, validationError: errors.join(', ') } };\n}\nreturn { json: { ...input, ...parsed, validationPassed: true } };"
},
"typeVersion": 2
},
{
"id": "2146e518-bbbf-4c05-bbb2-83fc7e9c5651",
"name": "Output Guardrails",
"type": "@n8n/n8n-nodes-langchain.guardrails",
"position": [
-1184,
1456
],
"parameters": {
"text": "={{ $json.suggestedResponse }}",
"guardrails": {
"nsfw": {
"value": {
"threshold": 0.8
}
},
"secretKeys": {
"value": {
"permissiveness": "balanced"
}
}
}
},
"typeVersion": 1
},
{
"id": "3c0cf427-51c0-4895-ad7f-e324f51efef4",
"name": "Respond - Input Blocked",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
-1696,
1744
],
"parameters": {
"options": {
"responseCode": 400
},
"respondWith": "json",
"responseBody": "={{ JSON.stringify({ status: 'input_blocked', message: 'Your message could not be processed. Please rephrase and try again.' }) }}"
},
"typeVersion": 1.1
},
{
"id": "7b874469-8620-433a-8e16-f4449781a278",
"name": "OpenRouter Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
"position": [
-1120,
1680
],
"parameters": {
"model": "google/gemini-3-flash-preview",
"options": {}
},
"credentials": {
"openRouterApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "dae51f1b-2d3f-42fd-8a74-fc0bfeb9acb1",
"name": "OpenRouter Chat Model1",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
"position": [
-1696,
1568
],
"parameters": {
"model": "google/gemini-3-flash-preview",
"options": {}
},
"credentials": {
"openRouterApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "2ebce685-2ada-4dd1-87f2-c89d7f5454b4",
"name": "Respond - Output Blocked",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
-832,
1456
],
"parameters": {
"options": {
"responseCode": 400
},
"respondWith": "json",
"responseBody": "={{ JSON.stringify({ status: 'input_blocked', message: 'Your message could not be processed. Please rephrase and try again.' }) }}"
},
"typeVersion": 1.1
},
{
"id": "b3c38b11-0c9a-4a6e-92f3-121c57f929eb",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3536,
1264
],
"parameters": {
"width": 624,
"height": 912,
"content": "## Complete Customer Feedback Pipeline\n\n### How it works\nThis workflow chains all five stages of the hybrid deterministic + AI pattern:\n1. **Intake** (Deterministic): Webhook receives feedback, Code node normalizes it, IF node validates required fields.\n2. **Input Guardrails** (Deterministic): Guardrails node checks for PII, injection attempts, and blocked keywords. Flagged inputs are blocked.\n3. **AI Classification + Drafting** (AI): AI Agent classifies the feedback (product issue, feature request, complaint, praise) and drafts a personalized response.\n4. **Output Validation** (Deterministic): Code node validates the classification. Guardrails node checks the draft for policy violations.\n5. **Routing** (Deterministic): High-confidence, valid results route by type: product issues create Jira tickets, feature requests go to backlog, complaints escalate to CS, and praise is flagged for testimonials. Low-confidence results queue for human review.\n\n### Setup\n- Connect your **LLM credentials** to the Chat Model nodes (AI Agent + both Guardrails nodes)\n- Copy the Webhook test URL and send sample feedback with varying tone and content\n- Review the **Route by Type** Switch node to map categories to your actual team integrations\n\n### Customization\n- Replace placeholder Code nodes (Jira, Backlog, Escalate, Testimonial) with real integrations\n- Tune confidence thresholds in the **High Confidence + Valid?** node as you validate accuracy\n- Add more feedback categories by extending the AI prompt and the Route by Type switch\n\nThis template is a learning companion to the Production AI Playbook, a series that explores strategies, shares best practices, and provides practical examples for building reliable AI systems in n8n. \n\n\nThis template is a learning companion to the Production AI Playbook, a series that explores strategies, shares best practices, and provides practical examples for building reliable AI systems in n8n. \n\nhttps://go.n8n.io/PAP-D&A-Blog"
},
"typeVersion": 1
},
{
"id": "1e649a11-e414-4b21-b833-ab85ea22cf6a",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2864,
1520
],
"parameters": {
"color": 7,
"width": 448,
"height": 544,
"content": "## Intake & Normalize"
},
"typeVersion": 1
},
{
"id": "a733f629-82ac-4d5b-9d48-1dba94a415f7",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2384,
1440
],
"parameters": {
"color": 7,
"width": 544,
"height": 624,
"content": "## Input Guardrails"
},
"typeVersion": 1
},
{
"id": "8dd60983-46fb-46a4-a230-f223d6d5b5b7",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1808,
1264
],
"parameters": {
"color": 7,
"width": 544,
"height": 640,
"content": "## AI Classification & Drafting"
},
"typeVersion": 1
},
{
"id": "b1dadf6d-de05-4f9a-8de4-81d1816890b3",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1232,
1184
],
"parameters": {
"color": 7,
"width": 544,
"height": 688,
"content": "## Output Validation"
},
"typeVersion": 1
},
{
"id": "60fe62ba-e773-4bfc-8660-88ba2eead7bd",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
-656,
800
],
"parameters": {
"color": 7,
"width": 704,
"height": 1072,
"content": "## Route by Type"
},
"typeVersion": 1
}
],
"connections": {
"Route by Type": {
"main": [
[
{
"node": "Create Jira Ticket",
"type": "main",
"index": 0
}
],
[
{
"node": "Add to Backlog",
"type": "main",
"index": 0
}
],
[
{
"node": "Escalate to CS",
"type": "main",
"index": 0
}
],
[
{
"node": "Flag as Testimonial",
"type": "main",
"index": 0
}
]
]
},
"Add to Backlog": {
"main": [
[
{
"node": "Respond - Result",
"type": "main",
"index": 0
}
]
]
},
"Escalate to CS": {
"main": [
[
{
"node": "Respond - Result",
"type": "main",
"index": 0
}
]
]
},
"Input Guardrails": {
"main": [
[
{
"node": "AI - Classify + Draft",
"type": "main",
"index": 0
}
],
[
{
"node": "Respond - Input Blocked",
"type": "main",
"index": 0
}
]
]
},
"Output Guardrails": {
"main": [
[
{
"node": "High Confidence + Valid?",
"type": "main",
"index": 0
}
],
[
{
"node": "Respond - Output Blocked",
"type": "main",
"index": 0
}
]
]
},
"Create Jira Ticket": {
"main": [
[
{
"node": "Respond - Result",
"type": "main",
"index": 0
}
]
]
},
"Normalize Feedback": {
"main": [
[
{
"node": "Has Required Fields?",
"type": "main",
"index": 0
}
]
]
},
"Validate AI Output": {
"main": [
[
{
"node": "Output Guardrails",
"type": "main",
"index": 0
}
]
]
},
"Flag as Testimonial": {
"main": [
[
{
"node": "Respond - Result",
"type": "main",
"index": 0
}
]
]
},
"Has Required Fields?": {
"main": [
[
{
"node": "Input Guardrails",
"type": "main",
"index": 0
}
],
[
{
"node": "Respond - Missing Fields",
"type": "main",
"index": 0
}
]
]
},
"Input Guardrails LLM": {
"ai_languageModel": [
[
{
"node": "Input Guardrails",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"AI - Classify + Draft": {
"main": [
[
{
"node": "Validate AI Output",
"type": "main",
"index": 0
}
]
]
},
"OpenRouter Chat Model": {
"ai_languageModel": [
[
{
"node": "Output Guardrails",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"OpenRouter Chat Model1": {
"ai_languageModel": [
[
{
"node": "AI - Classify + Draft",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Queue for Human Review": {
"main": [
[
{
"node": "Respond - Result",
"type": "main",
"index": 0
}
]
]
},
"High Confidence + Valid?": {
"main": [
[
{
"node": "Route by Type",
"type": "main",
"index": 0
}
],
[
{
"node": "Queue for Human Review",
"type": "main",
"index": 0
}
]
]
},
"Webhook - Feedback Intake": {
"main": [
[
{
"node": "Normalize Feedback",
"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.
openAiApiopenRouterApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
The full end-to-end workflow that chains all patterns together. This template processes customer feedback from intake to team routing, with normalization, validation, native guardrails, AI classification, and confidence-based branching at every step.
Source: https://n8n.io/workflows/13855/ — 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.
This template automatically generates a professional, structured title for newly-ingested Paperless-ngx documents using AI (OpenAI GPT), then removes a processing tag to mark the document as handled.
Protect your workflows with n8n's native Guardrails node, placed before and after your AI step. The input guardrails catch jailbreak attempts and PII before they reach your model. The output guardrail
The setup process is straightforward and typically takes less than five minutes. This involves connecting your preferred Large Language Model (LLM) and a live chat platform to the workflow via webhook
Complete PostgreSQL-backed system: Keyword scoring → AI research → Multi-part content generation → fal.ai Nano Banana image generation → WordPress publishing
⏺ 🚀 How it works