Purposes
Ask for specific models by name, without putting model names in your code.
A purpose is a name your application sends instead of a model name.
One route can hold several models. Answering a quiz question wants your strongest reasoning model, because a wrong answer is expensive and nobody checks it. Writing a conversational reply wants the cheap fast one, because any model writes an acceptable reply. Both come from the same backend, the same API key, and the same route. A purpose is the label that tells LatentKit which is which.
The point is the indirection. Your application sends "purpose": "quiz" and never names a
model. Tomorrow you point quiz at a different model in the console, and nothing in your
application changes.
What a purpose is not
A purpose is a preference, not a restriction.
Tagging two models with quiz does not mean only those two may answer a quiz request.
It means they are tried first. If they are unhealthy, over budget, or removed from the
route, the request still runs on the rest of the route rather than failing — one bad model
must never take your application down.
The response tells you which happened, so this is never silent. See Reading what actually happened.
The three routing axes
Three things decide where a request goes. They apply in this order:
| Axis | Question it answers | Set by | Values |
|---|---|---|---|
| Capability | What kind of request is this? | The endpoint, automatically | text, vision, audio_input, … |
| Purpose | Which models should handle it? | You, from labels your route declares | Whatever you name them |
| Response profile | How hard should that model think? | You, when the route allows overrides | fast, balanced, thinking |
Capability is a hard filter — a text-only model never serves a transcription request, and no label overrides that. Purpose and response profile are both preferences: they change the order candidates are tried in, never the set.
Purpose outranks response profile. You picked specific models for this work; an effort tag must not pull a different model ahead of them.
How it works
A route holds models. You declare purposes on the route and tag the models that serve each one:
Route "QuizFill" (strategy: priority)
1. grok-reasoning purposes: quiz
2. gemini-flash purposes: chat
3. deepseek-chat purposes: quiz
4. gpt-5.5-mini purposes: —| Request | Models tried, in order |
|---|---|
purpose: "quiz" | grok-reasoning → deepseek-chat → gpt-5.5-mini → gemini-flash |
purpose: "chat" | gemini-flash → gpt-5.5-mini → grok-reasoning → deepseek-chat |
| no purpose | grok-reasoning → gemini-flash → deepseek-chat → gpt-5.5-mini |
Three things are worth reading off that table.
The route's own order still applies. QuizFill is a priority route, so within each
group the models stay in rank order. On a cost-based route they would be in cost order
instead. A purpose narrows which models are preferred; it never imposes an order of its
own, and there is no separate ordering to configure.
Untagged models are the first fallback. gpt-5.5-mini carries no tags, so it is
general-purpose and safe for anything. Models tagged for a different purpose come last —
you earmarked gemini-flash for chat, so it should not be an equal fallback for a quiz.
No purpose means tags are ignored completely. The third row is the plain route order. Tagging a model never removes it from ordinary traffic.
Purposes are optional
You never have to create one. A route with no purposes behaves exactly as it always has,
and an application that sends no purpose is unaffected by any purpose you add later.
Reach for purposes when one route holds several models that do the same kind of work but you want specific ones for specific jobs. If your models differ by capability — a chat model and a transcription model — you need nothing: the endpoint already picks correctly.
Configuring them
Purposes live on the route, next to the models they select — open the route in the console and use the Purposes section. Declare a purpose (a name and an optional description), then tick the models that serve it.
Because they belong to the route, every app assigned to that route offers the same labels. Purposes publish atomically with the route, so a purpose and the models it references can never drift apart.
Naming
A purpose name describes the work, not the model, the speed, or the price:
| Good | Why |
|---|---|
extraction | Describes the job. Survives a model change. |
quiz | Describes the job. |
summarisation | Describes the job. |
| Avoid | Why |
|---|---|
grok | Names a model. The whole point is not naming models. |
fast | That is what response profiles are for, and it costs you the ability to ask for a fast version of this work. |
cheap | Same problem. |
gpt4-route | Names a model and a route. |
Names are 1–40 characters: a lowercase letter, then lowercase letters, digits, -, or
_. The lk- and lk_ prefixes are reserved for future platform purposes.
Finding your purposes
Purposes are declared per route, so a label that is right for one LatentKit account is silently wrong for another. Do not hardcode one you read in these docs. Ask your gateway which labels it accepts:
curl https://ai.latentkit.com/v1/me \
-H "Authorization: Bearer $LATENTKIT_API_KEY"{
"app": { "id": "app_1", "slug": "quizfill", "name": "QuizFill" },
"purposes": [
{ "purpose": "quiz", "description": "Answer quiz questions", "is_enabled": true },
{ "purpose": "chat", "description": "Conversational replies", "is_enabled": true }
],
"routing_contexts": [
{
"capability": "text",
"route": "QuizFill",
"is_default": true,
"purposes": [ { "purpose": "quiz", "description": "Answer quiz questions", "is_enabled": true } ]
}
]
// …policy, tenant, credits, latest_request
}purposes lists the labels on your app's default route. routing_contexts matters when
your app has a capability override — a rule sending, say, audio requests to a different
route. Purposes belong to whichever route actually runs, so a transcription purpose appears
under the audio_input context rather than the default one. It is always an array; []
means none apply.
An is_enabled: false entry is configured but turned off. Requests naming it use normal
route order — "turned off" and "never configured" have different fixes, so they are
reported differently.
Sending one
curl https://ai.latentkit.com/v1/chat \
-H "Authorization: Bearer $LATENTKIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"messages": [{"role": "user", "content": "Which planet is largest?"}],
"purpose": "quiz"
}'If you cannot change the request body — some CMS integrations cannot — send the
X-LK-Purpose header instead. The body wins when both are present.
A malformed or reserved label is a 400: we can name that mistake precisely, so failing
loudly helps you. A well-formed label your route does not declare is not an error — it
degrades, because that is usually a deployment-ordering problem and failing there would
take your application down for a config lag.
Reading what actually happened
Every response carries three fields:
{
"lk_requested_purpose": "quiz",
"lk_applied_purpose": "quiz",
"lk_purpose_source": "request"
}lk_applied_purpose is null unless a model tagged with your purpose produced the
answer. It is never set optimistically. Comparing it against what you sent is the only
way to tell "the model I chose answered" from "a fallback did".
lk_purpose_source | Meaning |
|---|---|
request | A model tagged with your purpose answered. |
default_no_purpose | No purpose was sent. |
fallback_purpose_exhausted | Tagged models were unavailable; another model answered. |
purpose_unknown | The route does not declare this label. |
purpose_disabled | The label exists but is turned off. |
purpose_no_capable_route | No tagged model can serve this endpoint. |
purpose_not_entitled | Your plan does not include purpose routing. |
A degraded purpose produces exactly the same routing outcome as the identical request carrying no purpose at all. It never independently causes a failure.
Watch fallback_purpose_exhausted and purpose_unknown in particular. The first means
your chosen models keep being unavailable; the second usually means an application shipped
a label before someone configured it. Both succeed, so neither shows up as an error — the
analytics view breaks requests, cost, and degradations down per purpose.
Alert on them. Because both are 200s from the wrong model, no error-rate alert will ever fire; count them in your own metrics and page when either is non-zero for a purpose that matters. The exact rule and a copy-paste snippet live in the routing observability playbook.
Purposes and response profiles
They answer different questions and compose freely:
{ "purpose": "quiz", "response_profile": "thinking" }Purpose picks the model. The profile asks it to think harder, where the model supports it. A model that always reasons and a model with no effort dial both exist, and the response never claims an effort level LatentKit did not actually set — see Response profiles.
Never name a purpose after effort. If you create quiz-fast, you have spent your purpose
namespace on something the profile axis already does, and you cannot then ask for a
thorough version of the same work.