Shaduf.Research preview
Jev: Use Cases, Alternatives & Products/Support-ticket triage with Jev
Use casesFive projects read at pinned commits

Support-ticket triage with Jev (TypeSafe AI): what real projects do when Jev fails, and a safe design

Routing support tickets to the right queue is a frequent concrete Jev use case: GitHub repository searches for "jev triage", "jev ticket" and "jev support" returned 272, 70 and 158 results on 1 Oct 2026, not all of them relevant. This page compares five public ticket-triage projects on one question: what happens to a ticket when Jev is unsure, returns an error, returns something malformed, has no key or hangs. Every cell comes from the project's source code at a stated commit. From that comes an eight-rule safe design.

Key finding

Four of five ticket-triage projects read in source send unsure tickets to a person. The fifth ignores queue confidence, and on any Jev API error it files the ticket in its archive lane.

Documentedsource at pinned commits, checked 1 Oct 2026

These are five projects out of at least 70 more triage repositories that were not read, so this is not a survey of how triage is usually built. None of the five takes a refund or account action from a Jev answer. None is independently verified as operating for real customers: two are helpdesk integrations (Zammad), three are demos or a command-line tool.

What happens to a ticket when something goes wrong

Open a situation to highlight its column in the table and read the safe pattern for it; opening one closes the others. Documented Read from source at the commits shown; README descriptions were not used.

Jev is unsureConfidence below the project's threshold

Four projects gate the queue choice on confidence and send the rest to a person: thresholds 0.5 (B), 0.55 (C), 0.6 (E) and 0.9 (D). A has no gate on its department choice: the chosen team is always used. Safe pattern: below your threshold, send the ticket to a person with the best guess and probabilities attached, as C does with an internal note.

API errorThe request fails with an HTTP error

Only D treats an error as "no decision": it returns 503, leaves the ticket unchanged and lets Zammad retry ("Never return a successful decision on model or API errors"). A files the ticket in its archive lane. B and C return 502 and leave the ticket unrouted (C without a note). E stops the whole batch. Safe pattern: hold the ticket for retry or a person; never map an error to a real queue.

Malformed answerMissing fields or values out of range

Only D validates the answer: the model id must match, the choice must be one of the options and the probability must be a finite number in [0, 1]. A does not validate, so a missing or non-numeric field ends in archive. B, C and E raise an error. Safe pattern: validate before acting and treat a failed check as an error.

No API keyThe key is not set

B, C and E refuse to start or exit. A returns HTTP 500 per request. D returns 503 so the source retries. All five fail visibly; none silently skips Jev. Safe pattern: fail loudly at start-up.

Jev hangsNo answer within a timeout

Two of five set no timeout (A uses a bare fetch, C uses HTTParty's default). D sets 20 s; B and E rely on the official SDKs' defaults. Safe pattern: set a timeout you chose, or know your SDK's defaults (see errors and rate limits).

Project (commit)Jev unsureAPI errorMalformed answerNo keyTimeoutSent to Jev
A. CyrilBaah/jev-triage-desk (63913177)Runnable demo (Next.js); direct fetch to /v1/systemone, jev-latest. Tests: noneno-threshold: no gateThe chosen team is always used; lanes by spam, escalate and urgency scoresfallback-archive laneException caught; reason "error"fallback-archive laneNo validation; a missing or non-numeric field ends therefail-closed: HTTP 500"TYPESAFE_API_KEY is not set"None setBare fetchTicket body. In the agent lane, subject and body also go to Groq for a draft reply
B. tuhinmitra888/ai-jev-ticket-triage (3e8b83a8)Runnable demo; official JS SDK @typesafe-ai/sdk. Tests: 6 policy tests, no networkheld: < 0.5 → human triageOption other → human toofail-closed: 502, not routed"triage failed"fail-closed: throws → 500No runtime checkfail-closed: server does not startClient built at module loadSDK defaultNot set in codeSubject, body; customer plan kept local
C. enderkus/zammad-jev-dispatcher (f999eca7)Zammad helpdesk integration; HTTP POST, jev-latest. Tests: noneheld: < 0.55 → stays "Unclassified"With an internal note: best guess and probabilitiesfail-closed: 502; stays UnclassifiedNo notefail-closed: raises → 502Unknown choice → manual-triage notefail-closed: does not startRaises at bootNone setHTTParty defaultTitle and body, HTML stripped; customer not sent
D. gbesse/zammad-jev-triage (5d2a0a78)Zammad helpdesk integration; pinned model jev-1.13.0. Tests: 3 test files and CIheld: < 0.9 or other → reviewMapped to a review groupfail-closed: 503; source retriesTicket unchangedfail-closed: validated → 503Model, option and probability checkedfail-closed: 503; source retries20 sSet explicitlyTitle and body, HTML stripped, at most 16,000 characters; public articles only
E. veermshah/jev-support-desk (aef8fa0c)Command-line tool; official Python SDK. Tests: 7 test files and CIheld: < 0.6 → triage reviewNoul answers in 0.35–0.65 flagged "uncertain"fail-closed: batch abortsExit code 2; one failure stops the runfail-closed: raises → exitTyped SDK responsefail-closed: exit code 2SDK default10 s per attempt, 30 s budgetSubject and message; tier and routing metadata kept out
  • fail-closed or held (a person or the host's own prompt decides)
  • fallback-<what> (named in the cell)
  • not recorded, not applicable or no-threshold (the cell says which)

Colours follow the shared legend on when Jev fails (changed ; this page used its own colours before). The Timeout column is a setting, not a verdict, so it is uncoloured.

What each project does automatically: A assigns a lane and shows an LLM-drafted reply (no send code found); B sets queue, priority and flags, and a refund request is only a flag; C and D change the Zammad group (C also posts an internal note); E sets labels and suggested actions only. Not one of the five refunds, cancels or changes an account from an answer.

Where each project draws the line

Queue-confidence threshold below which a ticket goes to a person

  • D. zammad-jev-triage0.90
  • E. jev-support-desk0.60
  • C. zammad-jev-dispatcher0.55
  • B. ai-jev-ticket-triage0.50
  • A. jev-triage-desknone
Bar length is the threshold on a 0–1 scale. C's threshold is set by the environment variable DISPATCH_CONFIDENCE_THRESHOLD (default shown). B also adds a review reason when urgency confidence is below 0.4. A routes on spam, escalate and urgency scores (lanes at 0.6, 0.6, 2.5 and 1.0) but never checks how sure Jev is of the department. How to choose your own is on confidence thresholds. Documented

A safe design, step by step

What your code should do with each Jev answer

  1. 1. Did the call fail? (no key, timeout, HTTP error)Yes: Hold the ticket for retry or a personNever map an error to a queue, archive or spam lane (D's 503 pattern)
  2. 2. Is the answer malformed? (missing key, option not in your list, probability outside [0, 1], wrong model)Yes: Treat it as an error and holdValidate as D does
  3. 3. Is the queue "other", or its confidence below your threshold?Yes: Send it to a person with the best guessAttach the probabilities, as C's internal note does; the five use 0.5–0.9
  4. OtherwiseApply the labelQueue and priority only; never a refund, cancellation or account change
Derived by the pool from the failure columns above; it is a design reading, not a measured result.

Eight rules, and which projects follow them

  1. An error is not a decision.

    On a missing key, timeout, HTTP error or malformed answer, hold the ticket for retry or a person. Never map it to a real queue, an archive lane or spam.

    DA archives
  2. Gate the queue choice on confidence.

    Below your threshold, send the ticket to a person with the best guess and probabilities. Tune the threshold on your own labelled tickets.

    BCDEA: no gate
  3. Offer an explicit "other / unclear" option and treat it as review.

    Tickets with no intent, or several, need somewhere to go.

    BDA, C, E: no "other"
  4. Validate before acting.

    The answer has the expected keys, the choice is one of your options, the probability is a finite number in [0, 1], and the model is the one you pinned.

    DA: none
  5. Set a timeout you chose.

    Or know your SDK's defaults (errors and rate limits).

    D: 20 sB, E: SDKA, C: none
  6. Send only what the decision needs.

    Subject and body, HTML stripped, length capped. Keep customer tier, IDs and contact details in your code. If a second model drafts replies, it receives the ticket too (A sends it to Groq). See what data leaves your system.

    BDE
  7. Jev answers set labels, never irreversible actions.

    None of the five refunds, cancels or changes accounts from an answer. Keep it that way.

    All five
  8. Test the routing policy offline.

    Write it as a pure function and test it without the network, including every failure branch (testing without a key).

    BDEA, C: no tests

Cost per 1,000 tickets depends on your token counts: use the cost per decision worksheet. No new prices were collected for this page.

How accurate is Jev at customer-service routing?

There is no independent ticket-accuracy test. The closest published figure is TypeSafe's own WorkflowEvals customer_service workflow (204 cases, "Customer-service routing and actions under three policies"). It scores agreement with LLM reference answers (GPT-6 Astra and Claude Fable 5.1), not accuracy against human-labelled tickets. Reported Graded as B01 on the benchmarks ledger.

  • Sol (workflow)78.3%
  • DS v4 flash76.8%
  • DS v4 pro76.1%
  • Jev (workflow)76.0%
  • Opus 572.4%
  • Sonnet 569.3%
  • Haiku 4.555.4%

TypeSafe lists Jev at $0.0001 per case and 0.4 s on this workflow. Read on evals.typesafe.ai, 1 Oct 2026, 05:35 UTC. A vendor's own test: use it as a starting point for your own labelled sample, not as ticket accuracy.

How the five were chosen

Three GitHub repository searches on 1 Oct 2026 ("jev triage", "jev ticket", "jev support": 272, 70 and 158 hits). From the results, five support-ticket projects with a real Jev call path were pinned to one commit each and read from source. No code was installed or run and no Jev call was made. Excluded: repositories that compare Jev with LLMs on synthetic tickets (for example benkohcc/jev-ticket-triage), a project using OpenJev instead of hosted Jev, and one using a local "jeff" sidecar. Several other repositories share common names such as jev-triage and were not read.

The same failure questions are asked of six moderation projects on content moderation (holding, removing and appeals) and of six model routers on model routing. Demo videos of ticket triage are listed on the videos page; the worked ticket example on What is Jev? explains a single request.

What was not verified

  • Live operation of any of the five projects: all are code-backed, none independently verified as operating.
  • The official JS SDK's default timeout (project B relies on it).
  • The remaining 70 or more triage repositories found by search.
  • Accuracy of any project's routing: no project publishes it, and the pool ran none.

Sources and check times (1 Oct 2026, UTC)

  • Projects, read at pinned commits (05:32): A (lib/jev/*.ts, lib/router.ts, app/api/triage/route.ts, lib/agent.ts); B (src/triage.ts, src/server.ts, test/decide.test.ts); C (app.rb, config/groups.yml); D (jev_core.py, app.py, webhook.py, policy.json); E (triage.py, cli.py, tickets.py, taxonomy.py).
  • Search: GitHub repository search for "jev triage", "jev ticket" and "jev support" (05:32).
  • Evidence box: evals.typesafe.ai and the WorkflowEvals README at commit 0ac3b8a (05:34).
  • SDK timeouts: Python SDK defaults as recorded on errors and rate limits.

Search published pools, pages, reports, and evidence.