Skip to main content

Routers

Create and manage AI Gateway routers, read what they did and learned, and score routed responses. A router is a virtual model: use its id or name as the model of a chat, completion or Responses request (/api/v1/ai-gateway/chat/completions, ...) and the gateway sends each request to one of its member models by its strategy. See Routers for every strategy.

All endpoints require authentication via the X-API-Key header and the scope shown.


Router Object​

{
"_id": "Tn3fcPqCPdFjjBoF5",
"name": "support-chat",
"description": "Customer support",
"strategy": "epsilon_greedy",
"members": [
{ "modelId": "6a502004c1c6457701e39093", "weight": 1, "priority": 1, "enabled": true,
"capabilities": [] },
{ "modelId": "2aed3be8ea864b6ef78ddf93", "weight": 1, "priority": 2, "enabled": true,
"capabilities": [] }
],
"fallbackBehavior": "next",
"stickyRouting": false,
"healthCheck": { "enabled": true, "intervalSeconds": 60, "failureThreshold": 3, "recoveryThreshold": 2 },
"banditConfig": { "rewardMetric": "feedback", "epsilon": 0.1 },
"organizationId": "org-default-1mgi3vfz8",
"owner": "FsbHXu34LHShtmb9L",
"createdAt": "2026-09-27T10:00:00.000Z",
"updatedAt": "2026-09-27T10:00:00.000Z"
}
FieldDescription
strategypriority, weighted, round-robin, latency-optimized, thompson_sampling, ucb1, epsilon_greedy, linucb, complexity, content-type, token-budget, semantic, routellm, canary, custom
members[].priorityLower is preferred (priority strategy, failover order, "most capable")
members[].weightRelative share of traffic (weighted)
members[].capabilitiesContent types the member handles: code, data, creative, ops, general (content-type)
fallbackBehaviornext: a failed member hands the request to the next by priority; error: never fail over
stickyRoutingWeighted: the same caller always gets the same member

Strategy settings​

FieldStrategyShape
banditConfigthompson_sampling, ucb1, epsilon_greedy, linucb{ reward_metric: success | latency | feedback, epsilon (epsilon_greedy, 0-1), exploration_bonus (ucb1 c, linucb alpha), max_latency_ms (latency), linucb_features (linucb) }
canaryConfigcanary{ control_model_id, canary_model_id, canary_percent }
semantic_configsemantic{ embedding_model_id, confidence_threshold, default_model_id, routes: [{ name, model_id, examples: [...] }] }
complexityConfigcomplexity{ simple_model_id, complex_model_id? } (unset complex: the most capable)
contentTypeConfigcontent-type{ type_model_map: { code | data | creative | ops | general: model_id }, default_model_id }
token_budget_configtoken-budget{ long_response_tokens, short_model_id }
routellm_configroutellm{ embedding_model_id (a text-embedding-3-small model), threshold }; exactly two members
customStrategycustom{ code }: Python that sets result to a member's model id

linucbFeatures are among prompt_length, last_message_length, conversation_turns, has_system_prompt, has_tools, json_output, has_images, complex_prompt, content_code, content_data, content_creative, content_ops.

A setting a strategy needs and is not given takes its default on save (epsilon 0.1, UCB1 exploration 1.41, LinUCB alpha 1.0, similarity threshold 0.75, long answers 500 tokens, RouteLLM threshold 0.11593, max latency 10000 ms). A router its strategy cannot act on is refused with a 400 that names what to fix.


GET /api/v1/ai-gateway/routers​

List routers the caller can use.

Scope: ai-gateway:read


POST /api/v1/ai-gateway/routers​

Create a router and return it (Router object).

Scope: ai-gateway:write

Body: name and strategy (required), description, members, fallbackBehavior, stickyRouting, healthCheck and the strategy's settings object.

{
"name": "support-chat",
"strategy": "linucb",
"members": [
{ "modelId": "6a502004c1c6457701e39093", "priority": 1 },
{ "modelId": "2aed3be8ea864b6ef78ddf93", "priority": 2 }
],
"banditConfig": {
"rewardMetric": "feedback",
"explorationBonus": 1.0,
"linucbFeatures": ["prompt_length", "has_tools", "content_code"]
}
}

GET /api/v1/ai-gateway/routers/:id​

Get a router.

Scope: ai-gateway:read


PATCH /api/v1/ai-gateway/routers/:id​

Update a router and return it. Send only the fields to change; the result is validated as the whole router. What a learning router has learned is kept (except that new LinUCB features start its learning afresh).

Scope: ai-gateway:write


DELETE /api/v1/ai-gateway/routers/:id​

Delete a router.

Scope: ai-gateway:write


POST /api/v1/ai-gateway/routers/:id/members​

Add a member and return the router: modelId (required), weight, priority, enabled, capabilities.

Scope: ai-gateway:write


DELETE /api/v1/ai-gateway/routers/:routerId/members/:id​

Remove a member. Refused when the router's strategy settings still name it.

Scope: ai-gateway:write


GET /api/v1/ai-gateway/routers/:id/stats​

What the router did and learned.

Scope: ai-gateway:read

NameInTypeRequiredDescription
periodHoursqueryintegerNoWindow in hours (default 24)

Response 200 OK

{
"data": {
"routerId": "Tn3fcPqCPdFjjBoF5",
"strategy": "epsilon_greedy",
"learns": true,
"rewardMetric": "feedback",
"periodHours": 24,
"totalDecisions": 40,
"members": [
{
"modelId": "6a502004c1c6457701e39093",
"decisions": 31, "share": 0.775,
"answered": 31, "failed": 0, "failedOver": 0,
"avgLatencyMs": 1840.2,
"rewarded": 28, "avgReward": 0.86, "pendingFeedback": 3,
"allTime": { "decisions": 120, "pulls": 110, "totalReward": 93.5, "avgReward": 0.85,
"lastReward": 1.0, "lastRewardAt": "2026-09-27T10:41:00+00:00" }
}
]
},
"meta": { "requestId": "req_abc123" }
}

decisions and share are the router's decisions in the window; all_time.pulls counts the rewards a member received. LinUCB routers add linucb: { features, weights } per member: the reward weight learned for each request feature.


GET /api/v1/ai-gateway/routers/:id/decisions​

The router's decisions, newest first.

Scope: ai-gateway:read

NameInTypeRequiredDescription
cursorquerystringNometa.nextCursor of the previous page; omit for the first page
limitqueryintegerNoDecisions per page (default 25)

Response 200 OK

{
"data": {
"total": 40, "offset": 0, "limit": 25,
"decisions": [
{
"id": "0f5c3a9e7d2b4c81a6e2b3d4f5a6b7c8",
"createdAt": "2026-09-27T10:41:00+00:00",
"selectedModelId": "6a502004c1c6457701e39093",
"reason": "Epsilon-greedy exploit: best average reward 0.860",
"success": true,
"servedModelId": "6a502004c1c6457701e39093",
"attempts": [{ "modelId": "6a502004c1c6457701e39093", "ok": true, "reason": null }],
"latencyMs": 1712.4,
"reward": 1.0, "rewardSource": "feedback", "rewardPending": false,
"feedback": { "reward": 1.0, "label": "resolved", "source": "api", "at": "2026-09-27T10:42:10+00:00" }
}
]
},
"meta": { "requestId": "req_abc123" }
}

POST /api/v1/ai-gateway/routers/:id/feedback​

Score a routed response of a router whose reward is feedback. The reward updates the chosen member's statistics, which the router's next decisions read.

Scope: ai-gateway:write

FieldTypeRequiredDescription
decisionIdstringYesrouter_decision.id of the routed response (also its X-Router-Decision-Id header)
rewardnumberYesFrom 0 (bad) to 1 (good)
labelstringNoA note kept with the feedback

Response 200 OK

{
"data": {
"decisionId": "0f5c3a9e7d2b4c81a6e2b3d4f5a6b7c8",
"routerId": "Tn3fcPqCPdFjjBoF5",
"modelId": "6a502004c1c6457701e39093",
"reward": 1.0,
"memberStats": { "pulls": 111, "totalReward": 94.5, "avgReward": 0.851 }
},
"meta": { "requestId": "req_abc123" }
}
StatusWhen
400No reward, or a reward outside 0 to 1
404No such decision for this router
409The router's reward is computed by the gateway (success, latency), the decision already has its reward, or its chosen member did not answer