← Blog

Claude API Customer Ticket Triage Automation

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

Support teams waste hours every day manually reading tickets, guessing at priority, and routing them to the right queue. Claude API customer ticket triage automation solves this by classifying incoming tickets — category, urgency, sentiment, and suggested team — in a single API call, before a human ever opens them. This article shows the actual architecture and code for building that pipeline.

The core idea is simple: every time a ticket arrives (from your helpdesk, email inbox, or chat widget), you send its text to Claude with a structured prompt asking for a JSON classification. The model returns category, priority, sentiment, and a short summary, which your backend then uses to route the ticket, set its priority flag, or auto-assign it to a team. No manual tagging, no rules engine with hundreds of if/else branches.

Why triage is a good fit for LLM automation

Traditional ticket routing relies on keyword rules ("if subject contains 'refund', route to billing") that break constantly — customers don't write in keywords, they write in sentences. Claude reads the actual meaning of a ticket: a message like "I was charged twice and nobody is answering me" should be flagged as billing + urgent + frustrated, even without the word "refund" or "angry" appearing anywhere.

This matters for three reasons:

Designing the classification schema

Before writing any code, define what fields you actually need downstream. A typical triage schema looks like:

{
  "category": "billing | technical | account | feature_request | other",
  "priority": "low | medium | high | urgent",
  "sentiment": "positive | neutral | negative | angry",
  "summary": "one-sentence summary of the issue",
  "suggested_team": "billing | support | engineering"
}

Keep the schema small. The more fields you ask for, the more likely the model drifts into inconsistent formatting. Five to seven fields is usually the sweet spot for ticket triage.

Building the triage call

The prompt should instruct Claude to return strict JSON and nothing else — this makes parsing reliable on your side.

const ticketText = `Subject: Double charge on my account
Body: I was charged twice this month for the Pro plan and support hasn't
responded in 3 days. This is the second time this has happened.`;

const response = 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: `Classify this support ticket. Return ONLY valid JSON with keys:
category, priority, sentiment, summary, suggested_team.

Ticket:
${ticketText}`
    }]
  })
});

const data = await response.json();
const classification = JSON.parse(data.content[0].text);
console.log(classification);

Running this against the example ticket should return something close to:

{
  "category": "billing",
  "priority": "urgent",
  "sentiment": "angry",
  "summary": "Customer double-charged for Pro plan, second occurrence, no response in 3 days.",
  "suggested_team": "billing"
}

That object maps directly to fields in your ticketing system (Zendesk custom fields, a Postgres column, whatever you use) — no further processing needed.

Handling batches and backlogs

If you're triaging a backlog instead of streaming live tickets, send them in small batches rather than one giant prompt. Classification accuracy drops when you cram 50 tickets into a single request and ask for 50 JSON objects back — the model starts losing track of which fields belong to which ticket. Process tickets individually or in groups of 3-5 with clearly numbered boundaries, and parallelize the requests instead.

async function triageTicket(ticket) {
  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: buildTriagePrompt(ticket) }]
    })
  });
  return JSON.parse((await res.json()).content[0].text);
}

const results = await Promise.all(tickets.map(triageTicket));

Where SubToAPI fits

Most triage pipelines like this one run as a background job or webhook handler sitting between your helpdesk and your team's routing logic. Instead of wiring up raw Claude credentials and managing them per-service, you issue a dedicated sub_live_... key through SubToAPI for the triage worker — separate from the key your chatbot or internal tools use. That gives you per-service usage metadata, so you can see exactly how many tokens your triage job burns versus everything else on the same Claude access, without extra accounting work. Setup takes a few minutes — see the quickstart and messages endpoint docs for the full request/response shape.

Routing and priority logic after classification

Once you have structured output, the routing layer is just normal backend code:

If ticket volume is high and you want classifications to appear as each piece of text is generated (useful for live dashboards), Claude's streaming mode works the same way here — see streaming for implementation details.

Common pitfalls

FAQ

Can Claude triage tickets in languages other than English?

Yes. Claude handles multilingual input well — you can ask it to classify tickets in their original language and still return the JSON schema in English, which keeps your downstream routing logic language-agnostic.

How accurate is LLM-based triage compared to manual tagging?

For common categories (billing, technical, account) accuracy is typically very high since these distinctions rely on clear context rather than ambiguous judgment calls. Edge cases — like tickets mixing multiple issues — still benefit from a human review step, so most teams route only high-confidence, non-urgent classifications automatically at first.

Do I need function/tool calling for this, or is a plain JSON prompt enough?

A plain JSON-output prompt is usually sufficient for ticket triage since the task is single-step classification. Tool calling becomes useful if triage needs to trigger actions directly, like creating a ticket in an external system — see tool use if you want the model to call functions as part of the pipeline.

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 →