This workflow corresponds to n8n.io template #13852 — we link there as the canonical source.
This workflow follows the Agent → OpenRouter 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": "e50e2e16-32d9-4ce4-baea-ba25970bf3db",
"name": "Webhook - Incoming Ticket",
"type": "n8n-nodes-base.webhook",
"position": [
-1440,
576
],
"parameters": {
"path": "route-ticket",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2
},
{
"id": "172223ae-9832-47b1-9731-b0ca55123e32",
"name": "Normalize Ticket",
"type": "n8n-nodes-base.code",
"position": [
-1216,
576
],
"parameters": {
"jsCode": "const raw = $input.first().json.body;\nreturn {\n json: {\n ticketId: raw.ticketId || 'TKT-' + Date.now(),\n subject: (raw.subject || '').trim(),\n body: (raw.body || raw.message || '').trim().substring(0, 2000),\n email: (raw.email || '').toLowerCase().trim(),\n timestamp: new Date().toISOString()\n }\n};"
},
"typeVersion": 2
},
{
"id": "8a1ff0c3-f97c-442e-a762-7aa4177ed961",
"name": "AI - Classify",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
-992,
576
],
"parameters": {
"text": "=Classify this support ticket.\n\nSubject: {{ $json.subject }}\nBody: {{ $json.body }}\n\nReturn ONLY valid JSON:\n{\"category\": \"billing|technical|sales|general\", \"urgency\": \"low|normal|urgent\", \"confidence\": 0.0 to 1.0}",
"options": {},
"promptType": "define"
},
"typeVersion": 1.7
},
{
"id": "262ba98c-e0cc-4c55-9abb-f92522260b5b",
"name": "Parse + Validate",
"type": "n8n-nodes-base.code",
"position": [
-640,
576
],
"parameters": {
"jsCode": "const raw = $input.first().json;\nlet parsed;\ntry { const text = typeof raw.output === 'string' ? raw.output : JSON.stringify(raw.output); const jsonMatch = text.match(/\\{[\\s\\S]*\\}/); parsed = JSON.parse(jsonMatch ? jsonMatch[0] : text); } catch(e) { parsed = { category: 'general', urgency: 'normal', confidence: 0 }; }\nconst validCats = ['billing','technical','sales','general'];\nif (!validCats.includes(parsed.category)) parsed.category = 'general';\nif (typeof parsed.confidence !== 'number') parsed.confidence = 0;\nreturn { json: { ...parsed, ticketId: $('Normalize Ticket').first().json.ticketId, email: $('Normalize Ticket').first().json.email } };"
},
"typeVersion": 2
},
{
"id": "0a29b0e6-9759-43d4-bb97-822621b0ccd2",
"name": "Confidence Threshold",
"type": "n8n-nodes-base.switch",
"position": [
-416,
560
],
"parameters": {
"rules": {
"values": [
{
"outputKey": "High Confidence",
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "c1",
"operator": {
"type": "number",
"operation": "gte"
},
"leftValue": "={{ $json.confidence }}",
"rightValue": 0.85
}
]
},
"renameOutput": true
},
{
"outputKey": "Medium Confidence",
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "c2",
"operator": {
"type": "number",
"operation": "gte"
},
"leftValue": "={{ $json.confidence }}",
"rightValue": 0.6
}
]
},
"renameOutput": true
},
{
"outputKey": "Low Confidence",
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "c3",
"operator": {
"type": "number",
"operation": "lt"
},
"leftValue": "={{ $json.confidence }}",
"rightValue": 0.6
}
]
},
"renameOutput": true
}
]
},
"options": {}
},
"typeVersion": 3.2
},
{
"id": "ffb130a2-0ba6-43f2-be3c-7b3ee667a719",
"name": "Route by Category",
"type": "n8n-nodes-base.switch",
"position": [
-192,
176
],
"parameters": {
"rules": {
"values": [
{
"outputKey": "Billing",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "c4",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.category }}",
"rightValue": "billing"
}
]
},
"renameOutput": true
},
{
"outputKey": "Technical",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "c5",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.category }}",
"rightValue": "technical"
}
]
},
"renameOutput": true
},
{
"outputKey": "Sales",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "c6",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.category }}",
"rightValue": "sales"
}
]
},
"renameOutput": true
}
]
},
"options": {}
},
"typeVersion": 3.2
},
{
"id": "62b71a99-910d-4d1e-a8b6-a3298221688d",
"name": "Flag for Review",
"type": "n8n-nodes-base.code",
"position": [
32,
576
],
"parameters": {
"jsCode": "return { json: { ...$input.first().json, action: 'flagged_for_review' } };"
},
"typeVersion": 2
},
{
"id": "3671dfc2-9862-4d96-9683-c97a9e32d88e",
"name": "Send to Human Queue",
"type": "n8n-nodes-base.code",
"position": [
32,
768
],
"parameters": {
"jsCode": "return { json: { ...$input.first().json, action: 'sent_to_human' } };"
},
"typeVersion": 2
},
{
"id": "03766d0e-16c7-42a1-ab66-a0b6753fd93e",
"name": "Billing Team",
"type": "n8n-nodes-base.code",
"position": [
32,
0
],
"parameters": {
"jsCode": "return { json: { ...$input.first().json, routed: 'billing_team' } };"
},
"typeVersion": 2
},
{
"id": "4ae34c34-7f16-4d2e-b2af-feeae04fbf5f",
"name": "Technical Team",
"type": "n8n-nodes-base.code",
"position": [
32,
192
],
"parameters": {
"jsCode": "return { json: { ...$input.first().json, routed: 'technical_team' } };"
},
"typeVersion": 2
},
{
"id": "11a66db0-a365-408c-953f-2154675cbf50",
"name": "Sales Team",
"type": "n8n-nodes-base.code",
"position": [
32,
384
],
"parameters": {
"jsCode": "return { json: { ...$input.first().json, routed: 'sales_team' } };"
},
"typeVersion": 2
},
{
"id": "e97e0dc7-340c-407e-b3e5-6ea74297e76a",
"name": "General Inbox",
"type": "n8n-nodes-base.code",
"position": [
32,
960
],
"parameters": {
"jsCode": "return { json: { ...$input.first().json, routed: 'general_inbox' } };"
},
"typeVersion": 2
},
{
"id": "74e4b921-5177-4618-bd89-e6ee95408080",
"name": "Respond",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
256,
480
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={{ JSON.stringify({ ticketId: $json.ticketId, routed: $json.routed || $json.action, category: $json.category, confidence: $json.confidence }) }}"
},
"typeVersion": 1.1
},
{
"id": "dfe71ff5-1259-414a-9106-cc260b403556",
"name": "OpenRouter Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
"position": [
-928,
800
],
"parameters": {
"model": "google/gemini-3-flash-preview",
"options": {}
},
"credentials": {
"openRouterApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "1fb7d84f-5cc8-475b-8fbe-772b35a0183c",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2128,
128
],
"parameters": {
"width": 576,
"height": 864,
"content": "## Classification-then-Routing\n\n### How it works\n1. **Webhook** receives an incoming support ticket.\n2. **Normalize Ticket** (Code node) cleans and standardizes the input.\n3. **AI - Classify** (AI Agent + OpenRouter) classifies the ticket by category and assigns a confidence score.\n4. **Parse + Validate** (Code node) checks that the AI output has valid categories and scores.\n5. **Confidence Threshold** (Switch node) routes by confidence level: high (>0.85) processes autonomously, medium (0.6-0.85) gets flagged for review, and low (<0.6) goes to a human queue.\n6. **Route by Category** (Switch node) fans high-confidence tickets to Billing, Technical, Sales, or General team handlers.\n\n### Setup\n- Connect your **LLM credentials** (e.g., OpenRouter, OpenAI) to the Chat Model node\n- Copy the Webhook test URL and send test tickets with varying complexity to see different confidence routing\n- Adjust confidence thresholds in the **Confidence Threshold** Switch node to match your risk tolerance\n\n### Customization\n- Replace the team handler Code nodes with real integrations (Slack, Jira, email)\n- Adjust thresholds over time as you validate AI accuracy: lower them to automate more\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\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": "18171911-4cd5-4971-9a86-c861475ea933",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1504,
288
],
"parameters": {
"color": 7,
"width": 432,
"height": 704,
"content": "## Receive & Normalize"
},
"typeVersion": 1
},
{
"id": "ce73ad96-e96b-416c-9bab-9164af4bbc7a",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1024,
288
],
"parameters": {
"color": 7,
"width": 304,
"height": 704,
"content": "## AI Classification"
},
"typeVersion": 1
},
{
"id": "fd237475-e0b1-43d7-acd2-eeff54c72076",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-688,
288
],
"parameters": {
"color": 7,
"width": 416,
"height": 704,
"content": "## Confidence Routing"
},
"typeVersion": 1
},
{
"id": "b2a3d9d5-9f58-44c3-a1cc-864f36c58d0a",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
-224,
-64
],
"parameters": {
"color": 7,
"width": 656,
"height": 1184,
"content": "## Category Routing"
},
"typeVersion": 1
}
],
"connections": {
"Sales Team": {
"main": [
[
{
"node": "Respond",
"type": "main",
"index": 0
}
]
]
},
"Billing Team": {
"main": [
[
{
"node": "Respond",
"type": "main",
"index": 0
}
]
]
},
"AI - Classify": {
"main": [
[
{
"node": "Parse + Validate",
"type": "main",
"index": 0
}
]
]
},
"General Inbox": {
"main": [
[
{
"node": "Respond",
"type": "main",
"index": 0
}
]
]
},
"Technical Team": {
"main": [
[
{
"node": "Respond",
"type": "main",
"index": 0
}
]
]
},
"Flag for Review": {
"main": [
[
{
"node": "Respond",
"type": "main",
"index": 0
}
]
]
},
"Normalize Ticket": {
"main": [
[
{
"node": "AI - Classify",
"type": "main",
"index": 0
}
]
]
},
"Parse + Validate": {
"main": [
[
{
"node": "Confidence Threshold",
"type": "main",
"index": 0
}
]
]
},
"Route by Category": {
"main": [
[
{
"node": "Billing Team",
"type": "main",
"index": 0
}
],
[
{
"node": "Technical Team",
"type": "main",
"index": 0
}
],
[
{
"node": "Sales Team",
"type": "main",
"index": 0
}
]
]
},
"Send to Human Queue": {
"main": [
[
{
"node": "Respond",
"type": "main",
"index": 0
}
]
]
},
"Confidence Threshold": {
"main": [
[
{
"node": "Route by Category",
"type": "main",
"index": 0
}
],
[
{
"node": "Flag for Review",
"type": "main",
"index": 0
}
],
[
{
"node": "Send to Human Queue",
"type": "main",
"index": 0
}
]
]
},
"OpenRouter Chat Model": {
"ai_languageModel": [
[
{
"node": "AI - Classify",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Webhook - Incoming Ticket": {
"main": [
[
{
"node": "Normalize Ticket",
"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.
openRouterApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Combine AI classification with confidence-based routing and category-based fan-out. This template classifies incoming support tickets, routes them by confidence level, and shows an example of how to fan high-confidence items out to the right team.
Source: https://n8n.io/workflows/13852/ — 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.
🧪 LABR - nuevo asistente (REPARADO). Uses httpRequest, postgres, postgresTool, toolCalculator. Webhook trigger; 63 nodes.
leads. Uses supabase, gmail, formTrigger, httpRequest. Webhook trigger; 62 nodes.
Brokeria-v20. Uses n8n-nodes-waha, httpRequest, redis, googleGemini. Webhook trigger; 56 nodes.
Brokeria-v15. Uses n8n-nodes-waha, httpRequest, postgres, redis. Webhook trigger; 55 nodes.
Transform your WhatsApp group conversations into actionable business intelligence through automated AI analysis and daily reporting. This workflow eliminates manual conversation monitoring by capturin