Routing tickets, emails and leads

Routing is the most common thing to hand a model, and the easiest to get wrong quietly. The shape that works: one multiple-choice question whose options are your actual queues, a probability threshold that decides whether to route automatically, and a person for everything else.

Write the queues as options

Start from the queues you really have, the ones with an owner and a process, not the categories you wish you had. Give each option a name your system already uses, and a one-line description of what belongs there. The model reads both; your code gets the name back and can route on it without a lookup table.

Descriptions do most of the work. "billing" is a word; "Invoices, charges, refunds and payment methods" draws a boundary. Where two queues touch, say which side a common case falls on: if refunds live with billing rather than support, write that down, because the model can't know your org chart.

Always leave a way out

The probabilities are shared out among the options you list, so a message that fits none of them still gets assigned to one. Add an explicit option — "none of the above, or not enough information to tell" — and route it to triage. Without it, every out-of-scope message lands in whichever queue was closest, and nobody finds out until someone complains.

One inbound message, with the queue question and a check for urgency:
{
  "state": "Our invoice for March was charged twice and we need the duplicate refunded before month end.",
  "questions": {
    "queue": {
      "type": "category",
      "text": "Which queue should handle this message?",
      "options": {
        "billing": "Invoices, charges, refunds and payment methods",
        "technical": "The product not working as it should",
        "sales": "Buying, upgrading or renewing",
        "other": "None of the above, or not enough information to tell"
      }
    },
    "time_critical": {
      "type": "yes_no",
      "text": "Does the message give a deadline in the next seven days?"
    }
  }
}
The answers. A real response also carries the model name and the token usage.
{
  "answers": {
    "queue": {
      "type": "category",
      "answer": "billing",
      "probabilities": {
        "billing": 0.94,
        "technical": 0.01,
        "sales": 0.02,
        "other": 0.03
      },
      "certainty": 0.7925
    },
    "time_critical": {
      "type": "yes_no",
      "answer": "yes",
      "probabilities": {
        "yes": 0.88,
        "no": 0.12
      },
      "certainty": 0.4706
    }
  }
}

Billing at 0.94 clears any sensible threshold, so this one routes itself. The second question is what turns routing into triage: a deadline inside a week at 0.88, so it can go to the top of the billing queue rather than the bottom, and nobody had to read it first.

Ask everything you need in one call

The message is read once and billed once however many questions you attach, so a routing call can do more than choose a queue. Useful companions: how upset the writer sounds, whether they mention cancelling, which product line it concerns, whether a deadline is named, whether there's enough information to act at all. Each answer is independent, so adding one can't change the others, and you get everything your queue needs in a single round trip.

Set the threshold where the mistake hurts

Route automatically when the leading queue clears a threshold you measured, and send the rest to triage. Where to put it depends on what a wrong turn costs: a message moved between two support queues is a minor delay, while one routed away from a security inbox is not. Pick the number by labelling a couple of hundred real messages and sweeping it, which the guide on thresholds walks through.

A practical detail: measure per queue, not just overall. One badly-described option can be wrong most of the time while the average still looks healthy.

Close the loop

Routing gives you free labels: when a person moves a ticket out of the queue the model chose, that's a corrected example. Log the chosen queue and the probabilities alongside each ticket, and review the moves weekly. Most of them point at two options whose descriptions overlap, and a sentence of clarification fixes more than any threshold change.

Keep a couple of guardrails: never let an automatic route be invisible, so people can see and undo it, and re-run your labelled set after any wording change, since moving a description moves the probabilities.

Leads and other inboxes

The same shape covers inbound sales. Replace queues with stages, add a rating for how well the sender matches your target customer, and a yes-or-no for whether they asked about price. The answers decide who picks it up and how fast, and the ratings give you something to sort the day's list by.

Using one

SeaCat answers a routing call in one request: the message, up to 64 questions about it, and every answer with its probabilities. It's $0.20 per million input tokens, and the message is billed once however many questions you ask.

More guides