Intent router
Send each incoming message to the intent it means. You define intents, each with a name, a few example phrases and an optional target (the agent, workflow…
Intent router
Send each incoming message to the intent it means. You define intents, each with a name, a few example phrases and an optional target (the agent, workflow, tool or URL that should handle it). KiviDB embeds the examples into one of your databases. To route a message, KiviDB embeds it, finds the closest examples and returns the best intent with its score and target, or no match when nothing is close enough.
No language model is called, and the target is returned, never run: your application decides what to do with it.
Intent router is part of Kivi Synapse, KiviDB's building blocks for AI agents.
The data model
| Router | a name (unique in the organization), the database that keeps its examples, an embedding model, a threshold and top_k |
| Intent | a name (unique in the router), a description, a target and 1 to 100 example phrases |
| Target | {type, ref, args}: type is agent, workflow, tool, url or none; ref names it (a URL for url); args is any JSON object you want back with it |
The router's settings and its intents' names, descriptions and targets are kept by KiviDB Cloud. The examples and their vectors are kept in your database, under keys that start with the router's prefix:
| Key | Holds |
|---|---|
kividb:intent:{<router id>}:ex:<intent id>:<n> | one example: a hash with intent, text and vec |
kividb:intent:{<router id>}:i:<intent id> | the intent's example texts, in order (a list) |
kividb:intent:<router id>:idx | the router's search index over the examples |
They use the database's own memory: about 2 KB per example with the built-in model (384 dimensions). Deleting an intent deletes its examples; deleting the router deletes all of its keys and its index. Deleting the database deletes its routers with it.
Embedding models
Chosen when you create the router, and fixed once it has intents (their examples were embedded with it; make a new router to change it).
- Built-in, all-MiniLM-L6-v2 (the default), run inside KiviDB Cloud: 384 dimensions, no key, default threshold 0.40.
- Hosted, Titan Text Embeddings V2 (hosted models, billed per token): 256, 512 or 1024 dimensions, no key, default threshold 0.45.
- OpenAI, or an OpenAI-compatible endpoint (https): the model's dimensions, your key (stored encrypted, never shown again), default threshold 0.60.
The models score on different scales, so each has its own default threshold. With the built-in model, rewordings of an example typically score 0.4 to 0.9 and unrelated text under 0.2.
How scoring works
- The message is embedded with the router's model.
- The nearest examples are found by cosine similarity, across all intents (an exact search, not an approximate one).
- They are grouped by intent. An intent's score is its closest example's similarity, from 0 to 1. Several close examples do not add up; one very close example is enough.
- Intents are ranked by score and the best
top_kkept. - If the best score reaches the threshold, it is the
matchand the rest arealternatives. Otherwisematchisnulland every ranked intent is an alternative.
The same message against the same examples always gets the same answer. A
message that should go nowhere ("what is a good banana bread recipe" to a
support router) comes back with match: null: treat that as an answer, by
asking again or handing over to a person.
To tune a router, give each intent five to twenty varied phrasings, and use the console's Try it box: type messages, see every intent's score, and drag the threshold to see what each setting would do before saving it.
The API
Every call takes an API key or a
console session. :id is the router's id or its name.
GET /intent-routers: every router in the organization, with its intent countGET /instances/:id/intent-routers: the routers whose examples are in one databasePOST /intent-routers: create a routerGET /intent-routers/:id: the router, its intents and their examplesGET /intent-routers/:id/stats: the router and each intent's example count, without the examplesPATCH /intent-routers/:id: changename,description,thresholdortop_kDELETE /intent-routers/:id: delete it, its examples and its indexPUT /intent-routers/:id/intents/:intent: create or replace an intentDELETE /intent-routers/:id/intents/:intent: delete an intent and its examplesPOST /intent-routers/:id/match: route one messagePOST /intent-routers/:id/match-batch: route up to 32 messages
Create a router with the built-in model:
curl https://api.kividb.io/intent-routers \
-H "Authorization: Bearer $KIVIDB_API_KEY" \
-H "Content-Type: application/json" \
-d '{"instance_id": "<database id>", "name": "support"}'
Optional fields: description, embed_provider (local, the default;
kividb; openai; openai_compatible), embed_model, embed_base_url,
embed_api_key, dimensions, threshold and top_k (default 5).
Add an intent. Saving the same examples again does not re-embed them; changing them replaces all of the intent's examples:
curl -X PUT https://api.kividb.io/intent-routers/support/intents/billing \
-H "Authorization: Bearer $KIVIDB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"description": "Charges, invoices and payment methods",
"target": {"type": "agent", "ref": "billing-agent", "args": {"queue": "finance"}},
"examples": [
"Why was I charged twice this month?",
"I need a copy of my invoice",
"Can I update my credit card?",
"What is this charge on my statement?",
"How much will my next bill be?"
]
}'
Route a message. top_k and threshold are optional and override the
router's for this call:
curl https://api.kividb.io/intent-routers/support/match \
-H "Authorization: Bearer $KIVIDB_API_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "there is a double payment on my card"}'
{
"match": {
"intent": "billing",
"score": 0.5088,
"target": { "type": "agent", "ref": "billing-agent", "args": { "queue": "finance" } }
},
"alternatives": [
{ "intent": "cancel", "score": 0.331, "target": { "type": "workflow", "ref": "retention-flow" } },
{ "intent": "tech support", "score": 0.1542, "target": null }
],
"threshold": 0.4,
"top_k": 5
}
A batch takes {"texts": [...]} (1 to 32) and returns {"results": [{"text", "match", "alternatives"}, ...]} in the same order.
Errors: 409 instance_not_running when the database is stopped,
409 examples_missing when the router's index is gone from the database (for
example after a flush; save each intent again to restore it),
409 sharded_not_supported, 422 too_many_intents and 422 too_many_routers
at the limits, and 503 embedder_busy when the built-in model is busy (retry
after a moment).
MCP
The KiviDB MCP server has two tools for routers made in the console or the API:
list_intent_routers: the organization's routers.route_intent:router(id or name) andtext, with optionaltop_kandthreshold. Returns the same answer as the match call.
Limits
| Routers per organization | 20 |
| Intents per router | 200 |
| Examples per intent | 100, each up to 500 characters (duplicates are kept once) |
| Message to route | up to 2,000 characters |
| Messages per batch | 32 |
top_k | 1 to 20 |
| Threshold | above 0, up to 1 |
Intent routers are not available on sharded databases yet. A database's Intent router tab and the dashboard's Intent router section manage them in the console.
There is no extra charge: the examples use the database's own memory. Only the hosted embedding model, if you choose it, is billed per token.