Intent routing across many intents
An intent router reads a message and decides what the person wants, so your code can send it to the right place. With Jepela that is one choice question. The answer has the chosen intent, a probability for every intent and a confidence, instead of an LLM's text that you have to parse and cannot set a threshold on.
One choice question
This request is copied from the Examples page, where it was sent to a Jepela gateway on 2026-09-23:
{
"state": "Can I move my delivery for order 88213 to my office address?",
"questions": {
"intent": {
"type": "choice",
"instructions": "What does the customer want?",
"criteria": {
"order_status": "where an order is",
"change_order": "change an order already placed",
"return": "return or exchange",
"product_question": "what a product includes",
"complaint": "a grievance",
"other": "unclear or none of these"
}
}
},
"model": "jepela-english"
}
The recorded answer: change_order at 0.985, confidence 0.946. A few rules for the options, from the Choice page:
- Write each option as a short description of what it means; an option without one is matched by its name.
- Keep options mutually exclusive: overlapping descriptions split the probability.
- Add
otherwhen the intents may not cover every message; Jepela warns (no_other_option) when a choice has none. - For deep hierarchies, ask level by level, one choice per level.
Gate on confidence
The answer says what; the confidence says whether. A common pattern from the Patterns page: simple lookups go to code, domain questions to a specialist with the right context, and uncertain or complex messages to a person. In the intent routing cookbook (2026-09-23, jepela-english, on a test setup), the gate sent every confidence under 0.5 to a person:
| Message | Intent | Confidence | Handler |
|---|---|---|---|
| Where is my order 88213? | order_status | 1.00 | code: order system |
| Your app deleted three months of my notes… | complaint | 0.48 | person (uncertain) |
| hi | other | 0.28 | person (uncertain) |
The last two went to a person because the gate says so, not because the engine was wrong about them.
More than 20 intents
Up to 20 options are read by the engine in one pass. Above about 20, the engine's one-pass answer concentrates on a few options, so by default ("method": "auto") Jepela matches instead: the message and each intent's description become vectors, and the closest intent wins. The answer says "method": "match" and carries similarity, the best and second-best cosine. A choice can have up to 255 options.
Measured on 2026-09-27 on a test server, on 600 real bank messages with 77 intents that are close to each other:
| Method | Right | Tokens billed |
|---|---|---|
| Matching (the default above 20 options) | 65.2% | 23 |
| The engine reading all 77 options | 62.2% | 812 |
A matched choice is billed for the state and the memory lines it read, not for the options. Neither method is right on every message: measure on your own intents before you automate.
Matching finds the closest label; it does not judge a situation. For short choices read in context, the engine stays the default.
Two rounds with the engine
With "method": "engine" the engine reads every option. Above 20 it asks in two rounds: the options in groups of up to 20, then the group winners against each other. The answer then has "rounds": 2. Every round reads the state, and all of it is billed. The three settings, from the Request options page:
{"method": "auto"} // the default: choices of more than 20 options are matched, the rest go to the engine
{"method": "engine"} // the engine reads every option; above 20 options in two rounds
{"method": "match"} // every choice question is matched, whatever its size
Option order
The engine is sensitive to the order of the options: answers to a large choice can change when they are reordered. "robust": true asks each choice of up to 20 options in up to three fixed orders and averages them; the answer has agreement, the share of orders that picked the winner. Each order is billed. Measured through the gateway on 2026-09-23: six caller orders of the same options gave one answer every time. Above 20 options, robust does not apply.
Measure on your own labels
Send the right intent with POST /v1/feedback when you know it, and read GET /v1/quality for accuracy, calibration error and automate_at: for the accuracy you need, a threshold chosen on your labels, and whether thresholds chosen that way reached that accuracy on labels they were not chosen on, with the share of messages above them. Calibration is fitted on your own labels: from 50 labels, a choice gets a temperature, kept only if it helps on labels it was not fitted on. Changing a question's options starts its labels again from zero, so keep a golden set of known messages and run it before you add or rename an intent.
Limits
- Matching finds the closest label; it does not weigh a situation.
- The engine reads about 475 tokens of state on
jepela-english; a longer message is cut, and the answer says so. - English first.
"model": "jepela"picks the multilingual checkpoint for other languages, which is less measured; do not namejepela-englishfor them. - Text only: describe an image in the state.
- Answers near a threshold flip on small changes; for expensive actions, require two answers in a row.
Next steps
- Get a key: confirm your email address and pay by card to start at once, or wait for Jepela to approve the account; prepaid credit by card or invoice, $0.042 per million input tokens, output free.
- Choice and Request options: matching, two rounds and robust.
- Cookbooks: the intent routing script and its output.
- Quality: labels, calibration and golden sets.
- All use cases