work / FIG. 03
← all systemsAI Ticket Routing + SLA Control
E-commerce support inbox, ~350 emails a month. A chain-of-thought classifier assigns category, P1–P4 priority, owner and SLA deadline; a 15-minute watcher escalates L1 → L2 → L3 all the way to the CEO.
- email → routed in ~7 s
- 12/12 correct on the test set
- 3-level escalation — zero lost VIP requests
The scenario
An e-commerce support inbox scenario: a specialty-coffee store with a retail side and a wholesale line for cafés and restaurants, roughly 350 emails a month landing in one shared Gmail inbox — delivery status, payments, complaints, product advice, wholesale inquiries, spam. Before automation, managers sorted the inbox by hand, with no priority order and no tracking of response time.
| Pain | Consequence |
|---|---|
| Manual triage of a shared inbox | Hours to first response; a manager becomes a human router |
| VIP/B2B requests buried among routine questions and spam | Risk of losing the most valuable segment |
| No SLA or deadline tracking | Tickets sit for days, unnoticed |
| No visibility into what customers actually need | Owner decisions made blind |
What I built
Two n8n workflows sharing one Google Sheets ticket log.
SMK Ticket Routing (Gmail trigger, polls every minute) filters out autoreplies and noreply mail, then runs each ticket through a chain-of-thought classifier — GPT-4o-mini via OpenRouter, JSON mode, temperature 0.2. The prompt is five blocks: role, business context, a three-axis instruction (content / impact / urgency), a five-step chain of thought compressed into a 2–3 sentence reasoning field, and a strict response format. Six categories, each with a named owner — delivery, payment, product advice, complaint, wholesale, spam. Priorities carry fixed SLAs: P1 = 30 min (double charges, legal threats, a ruined B2B batch), P2 = 120 min, P3 = 8 h, P4 = 24 h — with a VIP rule that any B2B ticket is P2 at minimum. A Validate & Enrich step parses the model’s JSON with a fallback (malformed JSON degrades to P2 plus manual review) and computes the SLA deadline from a canonical priority-to-minutes map in code, not from whatever number the LLM proposes. Every ticket is logged to Sheets across 15 fields and posted to Telegram.
SMK SLA Watcher (schedule, every 15 minutes) reads open tickets, and for anything past its deadline escalates by how overdue it is: L1 immediately — a reminder to the ticket owner, L2 past 30 minutes — Support Lead, L3 past 2 hours — Owner/CEO. Each level fires exactly once: escalation_level is written back to the sheet so a later run never re-alerts the same tier.
Architecture
The diagram above matches the running workflows: Gmail feeds the classifier, which writes a fully-specified ticket — category, owner, priority, SLA deadline — to the shared sheet and notifies Telegram, while a separate 15-minute watcher reads that same sheet and escalates anything overdue through three tiers.
Hard lessons that shaped it
- The model’s confidence score turned out to be useless for routing. All 13 test tickets came back with
confidence: 0.95— the field carried no discriminating signal, so the architecture never routes on it. - SLA minutes have to come from code, not the model. The canonical priority → minutes map lives in the Validate & Enrich step; the LLM’s own number is discarded. An LLM is not a source of truth for a fixed business rule.
- Editing a live polling workflow resets its checkpoint. Mid-test, a workflow edit reset the Gmail trigger’s checkpoint and re-ingested an already-processed email as a duplicate ticket. The production fix: a
TicketedGmail label plus a-label:Ticketedfilter in the trigger, so idempotency lives in Gmail itself, not in workflow state. - n8n’s own footer leaked into the classifier. The first test email carried n8n’s outgoing-message signature into the model’s input as part of the “email body” — fixed by turning off
appendAttributionon the notification node.
Results
Metrics are measured in test runs — this is a demo build, not a client testimonial.
| Metric | Before | After |
|---|---|---|
| Time to route a ticket | hours (manual triage) | ~7 seconds |
| Classification accuracy (test set) | — | 12/12 correct category and priority |
| Tickets under SLA tracking | 0% | 100% (every ticket has a deadline) |
| Escalations in testing | — | 4 alerts across 3 tiers (L1×2, L2, L3), each exactly once |
| Spam in the team inbox | sorted by hand | auto-closed (4 of 13 test emails) |
| AI classification cost | — | ~$0.11/month at 350 tickets |
contact
Have a process that eats your team's hours?
Describe it in a couple of sentences — I'll reply with an automation plan: what to build, what it touches, and what it will save.