The word triage comes from the French trier, to sort. It was formalised as battlefield surgery: when far more people need help than there are hands to give it, treating arrivals in order kills people who could have been saved. So a surgeon sorts first — who dies without help right now, who can wait an hour, who needs only a bandage — and spends scarce time where it changes outcomes.
Emergency departments still work this way. A severity index puts each arrival into tiers, from resuscitation down to routine cases that can wait for a clinic. The tiers are not a diagnosis. They are a sorting decision made with incomplete information, on purpose, before the real work starts.
Support queues have the same shape and rarely admit it. Engineering hours are fixed. Tickets arrive emotional, unstructured, and unordered. Without sorting, senior engineers answer password resets while a silent data-corruption bug sits unread at the bottom of a flat inbox. Triage is the same job: sort by blast radius and urgency before spending time that cannot be spent twice.
Tiers, not inboxes
Most teams that survive scale end up with tiers, whether they name them or not:
graph TD User([Incoming request]) --> L0{L0: Self-service} L0 -- Unresolved --> L1[L1: Triage] L1 -- Auto-fixable --> Resolved([Resolved without a specialist]) L1 -- Needs a person --> L2[L2: Specialist queue] L2 -- Code fix needed --> L3[L3: Engineering] L3 -- Vendor defect --> L4[L4: External vendor]- L0 Self-service. Kill repetitive intents before a ticket exists: FAQs, status pages, an assistant grounded in your own docs. Nothing here should need a person.
- L1 Triage. First-contact sorting: strip emotion, extract the component and intent, attach context, assign a priority. Success is measured in triage accuracy, not tickets closed.
- L2 Specialists. Multi-variable troubleshooting: logs, replication, configuration. The people who know one system deeply.
- L3 Engineering. Code changes, root causes, hotfixes. The scarcest time in the building.
- L4 External. Failures that started outside your software: cloud outages, OS faults, payment gateways.
The point of the ladder is isolation. Each tier protects the one above it from noise. An AI employee is most valuable at L0 and L1 — deflecting what never needed a person, and enriching what did so a person starts with signal instead of raw emotion. We have written about the supervision side of that line in which actions an AI employee should do alone: read freely, draft cheaply, act alone only within limits you set, and wait for a person for anything irreversible.
The four steps every ticket passes through
Once a request escapes L0 and becomes a ticket, the same four steps repeat regardless of tooling:
sequenceDiagram autonumber actor C as Customer participant T as Triage participant K as Knowledge and history participant Q as Queue C->>T: Unstructured text and logs Note over T: 1. Intake and classify T->>K: 2. Enrich with context Note over T: 3. Prioritise by impact and urgency T->>Q: 4. Hand off with structure1. Intake and classify. A customer writes “the integration is totally hosed, fix it now.” The useful content inside that sentence is small: which component, what kind of ask (bug, question, feature, docs gap), which error string, what was already tried. A classifier that turns prose into a fixed label — high, medium, low; billing, technical, general — is the whole step. The classic failure is asking a language model for “one word” and trusting the prompt: models sometimes answer in sentences, so production graphs add a structural guard (in Wetel, an llm step with matchAnyOf and streaming off) and keep the raw label in its own variable for the router to read.
2. Enrich with context. A raw ticket has no telemetry. Before a human sees it, attach: is this one tenant or a spike across many? What do past root-cause notes and incident logs say about these symptoms? What tier is this customer on? Retrieval over your own docs and past resolutions — a knowledge base attached to the classifying step — is what turns “broken” into “94% similar to incident 8842, check clock drift.” Enrichment that fails should fail open: an ungrounded ticket routed correctly beats a grounded ticket stuck in a failed run.
3. Prioritise by impact and urgency, not emotion. Priority is impact times urgency. A service down for many customers outranks an angry message from one, every time. Many teams use four bands — critical, high, normal, low — with response targets attached. In a workflow this is a switch: ordered cases, most specific first, with a mandatory default path so nothing falls through silently. A case that names a variable the run never set counts as false and shows up in the trace, which is where misspelt variable names go to be found.
4. Hand off with structure. Information rots between tiers. A good hand-off carries observed versus expected behaviour, reproduction steps, environment facts (versions, payloads, error strings, trace ids), and what lower tiers already tried. In workflow terms this is a call out to your own system — a tool or action step that creates or updates the ticket — followed by a reply that tells the customer which queue they are in and when to expect an answer, then an end to the turn. Every branch should converge on one closing step so the customer always hears something.
Confidence decides whether a person is involved at all. A routine request the system recognises with high confidence can be fixed and closed at L0. Anything ambiguous keeps its tags and diagnostics and goes to a queue with them attached. “Not sure” is just another edge — ideally one that points at a person. We go deeper on that split in a model that decides, a workflow that acts.
The pause that must have three exits
Some escalations should wait for a person: paging an engineer at night, issuing a refund, contacting many customers. In Wetel that is an approval step: the run parks, every active workspace owner is notified, and the run resumes down exactly one of three labelled paths — approved, rejected, expired. All three are required, because a missing path would be a silent default.
Design the request the way you would want to receive it — a title that is the decision, a summary that makes opening anything else unnecessary — and design the expiry path as carefully as the approval path. Expiry is never approval: the safe choice is a reply that says nobody has decided yet and a human will follow up, not the risky action itself. The full mechanics are in what happens when nobody approves in time and the human approval walkthrough.
Two habits keep approvals honest. Start strict and loosen with evidence: if one kind of request is approved unchanged every time for weeks, it can move down a rung; if a third are edited or rejected, the workflow just taught you something cheaply. And test every branch without bothering anyone: draft tests simulate approved, rejected and expired so each path can be walked before customers ever see it. (Trying a workflow safely.)
“Done” needs evidence, not a summary
The most corrosive triage failure is a ticket marked done that is not done: a step failed quietly, the next step proceeded anyway, and the summary says complete. The fix is a record independent of the AI’s own account of itself.
In Wetel that record is the run trace: every run writes which nodes it visited, what each one stored, which case a switch matched, which chunks grounded an answer, and the exact outbound request on tool and action steps. Capture the run id while the conversation is live — there is no list of past runs to browse later — store it on your ticket, and check “done” against the trace rather than the reply. Start with what a run record is and how to read one and the observability reference. The end-to-end worked example, including what the trace looks like turn by turn, is the ticket triage recipe.
What this looks like in Wetel today
Live today:
- Collect-then-classify graphs: an
await_replystep gathers the problem across turns without restarting, anllmstep classifies withmatchAnyOf, aset_variablestep normalises values to real numbers with no model call, and aswitchroutes to queues. - Knowledge-grounded answers: a tenant-scoped knowledge base attached to an
llmstep, with retrieved chunk ids recorded in the trace. - Approval pauses with three required exits, draft simulation of each outcome, and in-app notifications to owners.
- Per-run traces, draft-time diagnostics for common graph mistakes, and bounded runs so a looping graph fails diagnosably instead of burning budget silently.
Not built yet, and we would rather you read it here first:
- No automatic urgency scoring. Priority bands are expressions you author in a switch, not something the platform assigns.
- No built-in SLA timers, duplicate-ticket detection, or thread summarisation. Those live in your ticketing system; the workflow calls into it.
- No email notice for approvals yet; in-app only.
- No automatic “ask first” policy by action kind. An approval pause exists only where someone adds the step.
That list is the gap between “a workflow can triage” and “the platform triages for you.” The first is live and is what the recipe demonstrates. The second is direction, not a dated promise. Our roadmap reading guide explains how to tell the two apart.
A checklist for any triage, ours included
- Is sorting separate from solving? Can you point at the step that classifies and the step that acts, or does one prompt do both?
- What can be resolved without a person, and who decided that? Is the line written per kind of action or left to the model?
- What must wait for a person, and which person? Can you name them? What happens if they never answer — and is “no answer” ever treated as yes?
- Does enrichment happen before a human sees the ticket? Blast radius, past incidents, customer tier — attached automatically or reconstructed by hand each time?
- Can priority be recomputed from the ticket? Impact and urgency recorded as values, not inferred from tone.
- Does the hand-off survive the transfer? Observed versus expected, repro steps, environment, prior attempts — or a forwarded thread and good luck?
- Can “done” be checked independently? A run record with ids from the systems written to, not a sentence saying it worked.
Sort first, under scarcity, on purpose. Everything downstream — deflection rates, response times, engineers who still read the queue — follows from that decision.
Related reference docs
- Triage a support ticket end to end: the complete 15-node graph this post describes, with trace output and draft tests
- Route a ticket: the API calls that turn that graph into a running agent
- Approval node reference: fields, the three required edges, expiry, and what the workflow receives on resume
- Human approval: a walkthrough: build the graph, test in a draft, decide from the inbox or API
- Switch, router or condition?: which branching step fits which decision
- Observability and debugging: run traces and what you can inspect
- Supervised or autonomous?: where the line belongs between acting alone and asking first
- A model that decides, a workflow that acts: probabilities, thresholds, and the “not sure” edge