{
  "id": "yUxR8qHi03y6NCZH",
  "name": "AI Support Ticket Classifier and Auto-Reply Agent using Claude and Gmail",
  "tags": [],
  "nodes": [
    {
      "id": "a6187e62-8b16-4547-8c46-da66e4edfde6",
      "name": "Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        0
      ],
      "parameters": {
        "color": 0,
        "width": 400,
        "height": 964,
        "content": "## AI Support Ticket Classifier and Auto-Reply Agent\n\nAutomates first-response to customer support emails. Claude classifies every ticket, drafts a reply, and routes based on urgency.\n\n### How it works\n\n1. Gmail Trigger polls your inbox every minute for new unread emails.\n2. Configure Settings holds all user-editable values in one place.\n3. Build Prompt formats the email subject, sender, and body into a structured Claude instruction.\n4. Classify with Claude returns category, priority, sentiment, a summary, a draft reply, and an internal note.\n5. Parse Output extracts those fields and adds a timestamp.\n6. Route by Priority branches urgent tickets to a Gmail draft and Slack alert, sends auto-replies to normal tickets, and logs low tickets without replying.\n7. Every branch ends with a Google Sheets log row for full reporting.\n\n### Setup steps\n\n- [x] **Gmail credentials** - Connect your Google account in the Gmail Trigger and both Gmail action nodes.\n- [ ] **Anthropic credentials** - Add your Anthropic API key to the Claude Sonnet Model node.\n- [ ] **Slack credentials** - Connect your Slack workspace in the Alert Support Team node.\n- [ ] **Google Sheets credentials** - Connect your Google account in all three log nodes.\n- [ ] **Sheet ID** - Replace YOUR_GOOGLE_SHEET_ID in Configure Settings.\n- [ ] **Slack channel** - Replace YOUR_SLACK_CHANNEL_ID in Configure Settings.\n- [ ] **Support email** - Replace YOUR_SUPPORT_EMAIL in Configure Settings.\n- [ ] **Activate** - Turn the workflow on when all credentials are saved.\n\n### Customization\n\nEdit the categories list in Build Prompt to match your industry. Adjust MAX_EMAIL_CHARS in Configure Settings to control how much of the email body Claude reads."
      },
      "typeVersion": 1
    },
    {
      "id": "cc0905a4-a234-475d-8ddf-4793f7d56592",
      "name": "Trigger Section",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        448,
        112
      ],
      "parameters": {
        "color": 7,
        "width": 476,
        "height": 280,
        "content": "## Inbox trigger and settings\n\nGmail polls for new unread emails once per minute. Configure Settings holds every value the user needs to edit so no other node requires changes."
      },
      "typeVersion": 1
    },
    {
      "id": "f4e2c7df-224c-400f-8167-052adda5a84f",
      "name": "Gmail Trigger",
      "type": "n8n-nodes-base.gmailTrigger",
      "position": [
        480,
        240
      ],
      "parameters": {
        "simple": false,
        "filters": {
          "q": "is:unread",
          "labelIds": [
            "INBOX"
          ]
        },
        "options": {},
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "0db7829f-b16a-47b6-b150-95799f12668d",
      "name": "Configure Settings",
      "type": "n8n-nodes-base.code",
      "position": [
        720,
        240
      ],
      "parameters": {
        "jsCode": "// Edit all user settings here. Do not edit any other node.\nconst settings = {\n  SLACK_CHANNEL_ID: 'YOUR_SLACK_CHANNEL_ID',\n  GOOGLE_SHEET_ID: 'YOUR_GOOGLE_SHEET_ID',\n  SUPPORT_EMAIL: 'YOUR_SUPPORT_EMAIL',\n  SHEET_NAME: 'Support Tickets',\n  MAX_EMAIL_CHARS: 2000\n};\n\nreturn items.map(function(item) {\n  return { json: Object.assign({}, item.json, { settings: settings }) };\n});"
      },
      "typeVersion": 2
    },
    {
      "id": "d310566a-2f59-4b34-9fb2-11c92a9d960c",
      "name": "AI Analysis Section",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        960,
        112
      ],
      "parameters": {
        "color": 7,
        "width": 640,
        "height": 472,
        "content": "## Claude AI analysis\n\nBuild Prompt assembles the email into a structured instruction. Claude returns a JSON object with category, priority, sentiment, summary, draft reply, and internal note. Parse Output extracts those fields for routing."
      },
      "typeVersion": 1
    },
    {
      "id": "93de7cd9-d817-4ebc-a886-153222af6b05",
      "name": "Build Prompt",
      "type": "n8n-nodes-base.code",
      "position": [
        992,
        240
      ],
      "parameters": {
        "jsCode": "var item = items[0].json;\nvar settings = item.settings;\nvar subject = item.subject || 'No subject';\nvar fromVal = item.from;\nvar fromEmail = '';\nif (fromVal && fromVal.value && fromVal.value.length > 0) {\n  fromEmail = fromVal.value[0].address || '';\n} else if (typeof fromVal === 'string') {\n  fromEmail = fromVal;\n}\nvar rawBody = item.text || item.snippet || '';\nvar bodyTruncated = rawBody.length > settings.MAX_EMAIL_CHARS ? rawBody.slice(0, settings.MAX_EMAIL_CHARS) : rawBody;\n\nvar prompt = 'You are a customer support AI agent. Read the email below and respond ONLY with a valid JSON object, no other text before or after it.\\n\\n' +\n  'FROM: ' + fromEmail + '\\n' +\n  'SUBJECT: ' + subject + '\\n' +\n  'BODY:\\n' + bodyTruncated + '\\n\\n' +\n  'Return exactly this JSON structure:\\n' +\n  '{\"category\":\"one of billing, technical, general, refund, or complaint\",' +\n  '\"priority\":\"one of urgent, normal, or low\",' +\n  '\"sentiment\":\"one of frustrated, neutral, or positive\",' +\n  '\"summary\":\"one sentence describing the issue\",' +\n  '\"draft_reply\":\"professional plain text reply to send to the customer, no HTML, no greetings with first name placeholders\",' +\n  '\"internal_note\":\"brief plain text note for the support team on how to handle this\"}';\n\nreturn [{ json: Object.assign({}, item, {\n  prompt: prompt,\n  fromEmail: fromEmail,\n  emailSubject: subject\n}) }];"
      },
      "typeVersion": 2
    },
    {
      "id": "4a6a6821-530f-468f-8be5-21ccb2b60686",
      "name": "Classify with Claude",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        1232,
        240
      ],
      "parameters": {},
      "typeVersion": 1.4
    },
    {
      "id": "bd22479b-c6ae-4368-bdc5-43d01935f44c",
      "name": "Claude Sonnet Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "position": [
        1232,
        448
      ],
      "parameters": {
        "model": "claude-sonnet-4-6",
        "options": {
          "temperature": 0.2
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "890c58b0-70fb-4d40-b189-c7b76638a8d5",
      "name": "Parse Output",
      "type": "n8n-nodes-base.code",
      "position": [
        1472,
        240
      ],
      "parameters": {
        "jsCode": "var item = items[0].json;\nvar rawText = item.text || '';\nvar parsed = {};\n\ntry {\n  var match = rawText.match(/\\{[\\s\\S]*\\}/);\n  if (match) {\n    parsed = JSON.parse(match[0]);\n  }\n} catch (e) {\n  parsed = {\n    category: 'general',\n    priority: 'normal',\n    sentiment: 'neutral',\n    summary: 'Could not parse AI response. Manual review needed.',\n    draft_reply: 'Thank you for contacting us. A member of our team will be in touch shortly.',\n    internal_note: 'AI parsing failed on this ticket. Please review manually.'\n  };\n}\n\nvar now = new Date();\nvar processedAt = now.toISOString();\n\nreturn [{ json: Object.assign({}, item, {\n  priority: parsed.priority || 'normal',\n  category: parsed.category || 'general',\n  sentiment: parsed.sentiment || 'neutral',\n  summary: parsed.summary || '',\n  draft_reply: parsed.draft_reply || '',\n  internal_note: parsed.internal_note || '',\n  processedAt: processedAt,\n  replySubject: 'Re: ' + (item.emailSubject || 'Your support request')\n}) }];"
      },
      "typeVersion": 2
    },
    {
      "id": "469c35ce-5e1a-4dd5-aa2e-0ad6bdd2205c",
      "name": "Routing Section",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1632,
        48
      ],
      "parameters": {
        "color": 7,
        "width": 1196,
        "height": 584,
        "content": "## Priority routing\n\nUrgent tickets create a Gmail draft and fire a Slack alert so your team can review and send. Normal tickets get an auto-reply immediately. Low-priority tickets are logged only. All three branches end at Google Sheets."
      },
      "typeVersion": 1
    },
    {
      "id": "7e39d4c8-6924-4f3a-a127-8e2a64b133f5",
      "name": "Route by Priority",
      "type": "n8n-nodes-base.switch",
      "position": [
        1680,
        240
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": false,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "cond-urgent-01",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.priority }}",
                    "rightValue": "urgent"
                  }
                ]
              }
            },
            {
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": false,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "cond-normal-01",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.priority }}",
                    "rightValue": "normal"
                  }
                ]
              }
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "86071f81-5546-48df-b302-b9042e6ca392",
      "name": "Create Gmail Draft",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1920,
        144
      ],
      "parameters": {
        "message": "={{ $json.draft_reply }}",
        "options": {},
        "subject": "={{ $json.replySubject }}",
        "resource": "draft"
      },
      "typeVersion": 2.1
    },
    {
      "id": "1bc4f6ec-792b-4e03-a9f6-d2a348d85408",
      "name": "Prepare Slack Alert",
      "type": "n8n-nodes-base.code",
      "position": [
        2144,
        144
      ],
      "parameters": {
        "jsCode": "var item = items[0].json;\nvar msg = 'URGENT support ticket received\\n' +\n  'From: ' + (item.fromEmail || 'unknown') + '\\n' +\n  'Subject: ' + (item.emailSubject || 'n/a') + '\\n' +\n  'Category: ' + (item.category || 'n/a') + '\\n' +\n  'Sentiment: ' + (item.sentiment || 'n/a') + '\\n' +\n  'Summary: ' + (item.summary || 'n/a') + '\\n' +\n  'Internal note: ' + (item.internal_note || 'n/a') + '\\n' +\n  'A draft reply has been created in Gmail. Review and send.';\nreturn [{ json: Object.assign({}, item, { slackMessage: msg }) }];"
      },
      "typeVersion": 2
    },
    {
      "id": "431f55e7-b294-4956-a167-8b51c4ba128d",
      "name": "Alert Support Team",
      "type": "n8n-nodes-base.slack",
      "position": [
        2368,
        144
      ],
      "parameters": {
        "text": "={{ $json.slackMessage }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json.settings.SLACK_CHANNEL_ID }}"
        },
        "otherOptions": {}
      },
      "typeVersion": 2.2
    },
    {
      "id": "4256cfe5-742e-4e6c-b292-0cc533d819fb",
      "name": "Log Urgent Ticket",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2592,
        144
      ],
      "parameters": {
        "columns": {
          "value": {
            "From": "={{ $json.fromEmail }}",
            "Status": "Draft Created - Slack Sent",
            "Subject": "={{ $json.emailSubject }}",
            "Summary": "={{ $json.summary }}",
            "Category": "={{ $json.category }}",
            "Priority": "={{ $json.priority }}",
            "Sentiment": "={{ $json.sentiment }}",
            "Timestamp": "={{ $json.processedAt }}",
            "Internal Note": "={{ $json.internal_note }}"
          },
          "mappingMode": "defineBelow"
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $json.settings.SHEET_NAME }}"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json.settings.GOOGLE_SHEET_ID }}"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "89539b8c-8073-48af-88e1-b7272c38d38b",
      "name": "Send Auto-Reply",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1920,
        304
      ],
      "parameters": {
        "message": "={{ $json.draft_reply }}",
        "options": {},
        "subject": "={{ $json.replySubject }}",
        "emailType": "text"
      },
      "typeVersion": 2.1
    },
    {
      "id": "c2c9d2af-295f-425c-a173-11ed6a0eccd6",
      "name": "Log Normal Ticket",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2144,
        304
      ],
      "parameters": {
        "columns": {
          "value": {
            "From": "={{ $json.fromEmail }}",
            "Status": "Auto-Reply Sent",
            "Subject": "={{ $json.emailSubject }}",
            "Summary": "={{ $json.summary }}",
            "Category": "={{ $json.category }}",
            "Priority": "={{ $json.priority }}",
            "Sentiment": "={{ $json.sentiment }}",
            "Timestamp": "={{ $json.processedAt }}",
            "Internal Note": "={{ $json.internal_note }}"
          },
          "mappingMode": "defineBelow"
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $json.settings.SHEET_NAME }}"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json.settings.GOOGLE_SHEET_ID }}"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "6ef3aa7d-d115-43a4-8e2b-cc56ac633e0f",
      "name": "Log Low Priority Ticket",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1920,
        464
      ],
      "parameters": {
        "columns": {
          "value": {
            "From": "={{ $json.fromEmail }}",
            "Status": "Logged - No Reply",
            "Subject": "={{ $json.emailSubject }}",
            "Summary": "={{ $json.summary }}",
            "Category": "={{ $json.category }}",
            "Priority": "={{ $json.priority }}",
            "Sentiment": "={{ $json.sentiment }}",
            "Timestamp": "={{ $json.processedAt }}",
            "Internal Note": "={{ $json.internal_note }}"
          },
          "mappingMode": "defineBelow"
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $json.settings.SHEET_NAME }}"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json.settings.GOOGLE_SHEET_ID }}"
        }
      },
      "typeVersion": 4.4
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "c76a3b46-b630-4a7a-9f6c-5947a158c1ca",
  "connections": {
    "Build Prompt": {
      "main": [
        [
          {
            "node": "Classify with Claude",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Output": {
      "main": [
        [
          {
            "node": "Route by Priority",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gmail Trigger": {
      "main": [
        [
          {
            "node": "Configure Settings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Auto-Reply": {
      "main": [
        [
          {
            "node": "Log Normal Ticket",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by Priority": {
      "main": [
        [
          {
            "node": "Create Gmail Draft",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send Auto-Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Alert Support Team": {
      "main": [
        [
          {
            "node": "Log Urgent Ticket",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Configure Settings": {
      "main": [
        [
          {
            "node": "Build Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Gmail Draft": {
      "main": [
        [
          {
            "node": "Prepare Slack Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Claude Sonnet Model": {
      "ai_languageModel": [
        [
          {
            "node": "Classify with Claude",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Slack Alert": {
      "main": [
        [
          {
            "node": "Alert Support Team",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Classify with Claude": {
      "main": [
        [
          {
            "node": "Parse Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}