← Blog

How to Build a Support Ticket Triage Bot

2026-10-28 · 5 min read · SubToAPI Team

A support ticket triage bot reads an incoming ticket, figures out what it's about, how urgent it is, and who should handle it — then tags or routes it automatically. You build one by sending the ticket text to a language model with a tightly defined classification schema, parsing the structured response, and wiring that output into your helpdesk's routing rules. The hard part isn't the AI call; it's designing the taxonomy and handling edge cases so the bot doesn't misroute angry customers or security reports.

This guide walks through the full pipeline: defining categories, prompting for structured output, calling the API, and connecting the result to your ticketing system.

Why triage is a good first automation project

Triage is repetitive, time-sensitive, and rule-based enough that an LLM can do it reliably — but nuanced enough that keyword matching fails. A ticket that says "the app crashed and I lost my invoice" is both a bug report and a billing issue. A human agent reads intent in seconds; a triage bot needs to be prompted to do the same thing consistently, at scale, 24/7.

Done well, triage automation:

Step 1: Define your taxonomy before writing any code

Resist the urge to let the model freelance. Decide upfront:

Keep the list short. Ten clear categories beat thirty overlapping ones. If two categories are genuinely ambiguous for humans, they'll be ambiguous for the model too.

Step 2: Design a prompt that forces structured output

The triage bot's prompt should instruct the model to return JSON matching your taxonomy exactly — no prose, no explanations unless you ask for a short reasoning field for debugging.

You are a support ticket triage assistant. Classify the ticket below.

Return ONLY valid JSON matching this schema:
{
  "category": "billing" | "bug" | "feature_request" | "account_access" | "security" | "general",
  "priority": "urgent" | "high" | "normal" | "low",
  "sentiment": "frustrated" | "neutral" | "positive",
  "summary": "one sentence summary for the agent",
  "needs_human_review": boolean
}

Rules:
- "security" always gets priority "urgent".
- Set needs_human_review to true if the ticket is ambiguous or mentions legal/refund disputes.

Ticket:
"""
{{ticket_text}}
"""

The needs_human_review flag matters more than people expect — it's your safety valve for cases the model isn't confident about, instead of silently misrouting them.

Step 3: Call the model and parse the response

Here's a minimal example calling a chat-style completion API and parsing the JSON reply:

async function triageTicket(ticketText) {
  const res = await fetch("https://api.subtoapi.app/v1/messages", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      model: "claude-sonnet-4-5",
      max_tokens: 300,
      messages: [
        { role: "user", content: TRIAGE_PROMPT.replace("{{ticket_text}}", ticketText) }
      ]
    })
  });

  const data = await res.json();
  const text = data.content[0].text;
  return JSON.parse(text);
}

If you're already using Claude for other parts of your app, SubToAPI lets you hit the same /v1/messages endpoint with an application key (sub_live_...) instead of managing provider credentials directly — useful once triage is one of several services calling the model. See the quickstart and messages docs for the full request format.

Step 4: Wire the output into routing logic

Once you have structured JSON back, routing is plain application code:

const result = await triageTicket(ticket.body);

if (result.category === "security") {
  await assignToQueue(ticket.id, "security-oncall");
} else if (result.needs_human_review) {
  await assignToQueue(ticket.id, "review-queue");
} else {
  await assignToQueue(ticket.id, categoryToQueueMap[result.category]);
}

await addTags(ticket.id, [result.category, result.priority]);
await setSentiment(ticket.id, result.sentiment);

This is the part that actually saves time — triage data flowing straight into your helpdesk's native tagging and assignment fields, no manual sorting.

Step 5: Handle volume and latency

If tickets arrive in bursts (post-incident, after a product launch), you'll want to batch calls and watch concurrency limits rather than firing one request per ticket synchronously. For a live agent dashboard, streaming responses token-by-token isn't usually necessary for triage — you want the full JSON object before you act on it — so a standard blocking request per ticket is the simpler and more reliable choice here.

Also budget for retries: if the model returns malformed JSON (rare but it happens), retry once with a stricter reminder appended to the prompt before falling back to needs_human_review: true and a human queue.

Step 6: Let the bot call tools, not just classify

A more advanced triage bot doesn't stop at classification — it can look up the customer's plan, check if there's an open incident matching the keywords, or pull recent order history before deciding priority. That means giving the model access to tool calls so it can query your systems as part of the triage decision, rather than working from ticket text alone. For example, a ticket mentioning "can't login" during a known outage should get auto-tagged with the incident ID instead of opening a fresh investigation.

Step 7: Monitor and tune

Track three numbers weekly: misrouted ticket rate, needs_human_review rate, and average time-to-first-response for triaged vs. non-triaged tickets. If needs_human_review creeps above 20-30%, your taxonomy or prompt needs tightening, not more model calls.

If you want to move from a one-off script to a team-wide triage service with API keys per environment and usage visibility, pricing starts at Solo for a single integration and scales to Team and Scale plans with per-seat API keys — handy when triage, summarization, and reply drafting all live behind the same account. You can try it from signup before committing to a plan.

questions

Does a triage bot replace human support agents? No. It handles classification and routing — the repetitive first step — so agents spend time on actual customer problems instead of sorting inboxes.

How do I stop the bot from misrouting ambiguous tickets? Add a needs_human_review flag to your schema and route anything flagged there to a human queue instead of forcing a guess. It's cheaper than a wrong routing decision.

Can the triage bot update tickets automatically in my helpdesk? Yes, if your helpdesk exposes an API for tags, priority, and assignment — the model's JSON output maps directly to those fields in your own integration code.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →