AutomationFlowsAI & RAG › Production AI Playbook: Deterministic Steps & AI Steps (3 of 5)

Production AI Playbook: Deterministic Steps & AI Steps (3 of 5)

ByElvis Sarvia @elvissaravia on n8n.io

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.

Webhook trigger★★★★☆ complexityAI-powered19 nodesAgentOpenRouter Chat
AI & RAG Trigger: Webhook Nodes: 19 Complexity: ★★★★☆ AI nodes: yes Added:

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 →

Download .json
{
  "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.

Pro

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 →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

🧪 LABR - nuevo asistente (REPARADO). Uses httpRequest, postgres, postgresTool, toolCalculator. Webhook trigger; 63 nodes.

HTTP Request, Postgres, Postgres Tool +9
AI & RAG

leads. Uses supabase, gmail, formTrigger, httpRequest. Webhook trigger; 62 nodes.

Supabase, Gmail, Form Trigger +13
AI & RAG

Brokeria-v20. Uses n8n-nodes-waha, httpRequest, redis, googleGemini. Webhook trigger; 56 nodes.

N8N Nodes Waha, HTTP Request, Redis +7
AI & RAG

Brokeria-v15. Uses n8n-nodes-waha, httpRequest, postgres, redis. Webhook trigger; 55 nodes.

N8N Nodes Waha, HTTP Request, Postgres +7
AI & RAG

Transform your WhatsApp group conversations into actionable business intelligence through automated AI analysis and daily reporting. This workflow eliminates manual conversation monitoring by capturin

OpenRouter Chat, Output Parser Autofixing, Agent +6