Jev ticket triage: queue, severity and churn risk in one call
Ask one Choice question for the queue and one Score question for severity in the same call — both come back in roughly the time one of them would take, for a fraction of a cent.
Run this example
3 questions · no key neededThe problem
Ticket triage is high volume, latency-sensitive and boring: every inbound message needs a queue, a priority and sometimes a language tag. Doing it with a chat model means a prompt carrying every queue description, a JSON schema, a parse step and a retry path — seconds per ticket and real money at scale.
How Jev handles it
The state is the ticket text. A Jev ticket triage call asks every question you actually need, typed, in one request. Because questions are evaluated in parallel against the same state, asking three costs about what asking one costs.
from typesafe_sdk import Choice, Noul, NoulCriteria, Score, TypeSafeClient
client = TypeSafeClient(api_key=os.environ["TYPESAFE_API_KEY"])
response = client.system_one(
model="jev-latest",
state=ticket_body,
questions={
"queue": Choice(
instructions="Which team should handle this ticket?",
criteria={
"billing": "Payments, invoicing, refunds",
"technical": "Bugs, outages, integrations",
"account": "Login, permissions, plan changes",
"sales": "Pricing, upgrades, new accounts",
"spam": "Unsolicited or irrelevant",
},
),
"severity": Score(
instructions="How urgent is this ticket?",
criteria=["Low", "Normal", "High", "Urgent"],
),
"churn_risk": Noul(
instructions="Is the customer threatening to cancel?",
criteria=NoulCriteria(
true="Explicitly mentions cancelling or leaving",
false="No cancellation intent expressed",
),
),
},
)
answers = response.answers
if answers["queue"].confidence >= 0.85:
route_to(answers["queue"].choice, priority=answers["severity"].score)
else:
route_to("manual_review")State sent the example loaded in Run this example above, as it first appears.
Answers returned
- Latency (median of 3)
- 514.9ms
- Minus network floor (198.8ms)
- ≈316ms
- Input tokens
- 539
- Cost
- $0.00002264
A clear-cut billing ticket that also carries an explicit churn threat.
Writing the questions for Jev ticket triage
In Jev ticket triage the queue descriptions are the whole prompt, so write them the way you would brief a new support hire. Use the queue IDs your helpdesk already has as the option keys, and make each description say when a ticket belongs there rather than what the team does all day. Where two queues genuinely overlap — a refund after a plan downgrade could be billing or account — settle it inside one of the descriptions. Jev reads criteria literally and will not infer a house rule nobody wrote down. Keep spam as its own option too, so junk has somewhere to land other than a real queue.
Severity comes back as a weighted mean of the level indices, not as a label. The measured ticket scored 2.68 on the four-level rubric — 0.68 on Urgent, 0.32 on High — so round it for the priority band and keep the raw number for ordering: two tickets that both round to Urgent are not equally urgent, and sorting on the score puts the worse one first. The churn question earns its place because it rides on the same call; phrase it so that true is the case you act on.
What to put in the state
Jev ticket triage works on what the customer wrote, so send that and very little else: the subject, the newest message, and at most a line or two of facts you already hold, such as the plan they pay for. Strip signatures, legal footers and quoted reply chains before the call. They are the irrelevant detail TypeSafe warns drags accuracy down, and on a long thread they are most of the tokens.
A labelled object reads better than one pasted string: {"subject": …, "latest_message": …, "plan": "Pro"} lets a queue description refer to the plan by name. Screenshots and attached logs are invisible to a text-only model, so pull out what matters from them first or leave them to the human queue.
What one decision costs
A Jev ticket triage call is priced on the tokens you send; the three answers add nothing. The first line is the measured ticket above, questions included. The second assumes a longer email thread that still comes to 2,000 tokens after trimming. The third prices 100,000 tickets a month at the measured size.
# the measured ticket, three questions 539 tokens × $0.042 / 1M = $0.0000226 # an email thread trimmed to 2,000 tokens 2,000 tokens × $0.042 / 1M = $0.0000840 # 100,000 tickets a month at the measured size 100,000 × 539 = 53,900,000 tokens × $0.042 / 1M = $2.26
Trimming the thread is therefore a cost decision as much as an accuracy one, while adding the churn question — or a fourth question — barely moves the bill.
When not to use Jev for this
- The reply is the product. Jev decides where a ticket goes and how urgent it is; drafting the answer still needs a generative model, so a triage-and-reply flow runs two.
- Most of your queues work in languages other than English. English is where Jev is strongest; measure routing accuracy per language before trusting the confidence gate outside it.
- You already hold tens of thousands of labelled tickets for a taxonomy that no longer changes. A small model trained on those labels is the stronger choice for that one fixed job.
- Priority depends on arithmetic — hours left on an SLA, a refund above a limit, days since purchase. Compute it in code and put the result in the state; Jev is not reliable at counting or comparing dates.
Where it fits in your stack
Jev ticket triage belongs straight after the helpdesk webhook and before any assignment rule. Confident answers set queue and priority directly; anything under your threshold lands in a manual-review view with the probabilities attached, so the reviewer sees which two queues it was torn between. Only tickets that need a drafted reply go on to an LLM, and that model receives the queue and severity as context instead of re-deriving them.
Log each answer beside the queue a person finally chose. Those pairs are the labelled set for tuning option descriptions later — jev-align runs GEPA over exactly that — and for any classifier you train once the taxonomy settles. For a runnable starting point, typesafe-jev-examples includes a triage example that runs through OpenRouter.
Notes from the field
- Customer service was one of the four workflows in TypeSafe's own launch evaluation.
- The churn-risk Noul costs almost nothing to add — use spare questions for signals you would never have paid an LLM call to get.
- Gate on confidence and send the uncertain tail to a human or an LLM. That tail is also your best source of labelled training data.
Common questions
How accurate is Jev ticket triage?
On 27 labelled tickets we wrote, run three times, Jev routed all 27 correctly on every pass — and so did Mistral Small 3.2, so read that as parity rather than a win. What Jev adds is a confidence that drops when a ticket is genuinely ambiguous: 0.979 on average for the clear set, 0.841 for the ambiguous one. Method and tables on the benchmarks page.
Can it assign tickets to named agents rather than team queues?
Yes, if you can list them. A Choice takes up to 255 options and the list travels with each Jev ticket triage call, so it can be built from whoever is on shift. Past 255, choose the team first and the person second.
Why did the measured ticket come back at 1.0 confidence?
Because it is an easy one: a duplicate charge is billing by any definition, and easy tickets tell you nothing about where your threshold should sit. The 0.85 in the sample is a placeholder. Set the real one from your own labelled tickets, at the point where tickets above it are routed correctly often enough for what a misroute costs you.