curl --request PUT \
--url https://{host}/api/llm-gateway/admin/model-mappings/{id} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"bare_alias": true,
"change_note": "<string>",
"cooldown_429_default_secs": 123,
"cooldown_base_secs": 123,
"cooldown_failure_threshold": 123,
"cooldown_max_secs": 123,
"cooldown_overloaded_secs": 123,
"cooldown_window_secs": 123,
"enabled": true,
"model_alias": "<string>",
"priority": 123,
"request_timeout_secs": 123,
"retry_on_429_count": 123,
"retry_on_429_max_wait_secs": 123,
"stream_idle_timeout_secs": 123,
"upstream_model": "<string>"
}
'import requests
url = "https://{host}/api/llm-gateway/admin/model-mappings/{id}"
payload = {
"bare_alias": True,
"change_note": "<string>",
"cooldown_429_default_secs": 123,
"cooldown_base_secs": 123,
"cooldown_failure_threshold": 123,
"cooldown_max_secs": 123,
"cooldown_overloaded_secs": 123,
"cooldown_window_secs": 123,
"enabled": True,
"model_alias": "<string>",
"priority": 123,
"request_timeout_secs": 123,
"retry_on_429_count": 123,
"retry_on_429_max_wait_secs": 123,
"stream_idle_timeout_secs": 123,
"upstream_model": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.put(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PUT',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
bare_alias: true,
change_note: '<string>',
cooldown_429_default_secs: 123,
cooldown_base_secs: 123,
cooldown_failure_threshold: 123,
cooldown_max_secs: 123,
cooldown_overloaded_secs: 123,
cooldown_window_secs: 123,
enabled: true,
model_alias: '<string>',
priority: 123,
request_timeout_secs: 123,
retry_on_429_count: 123,
retry_on_429_max_wait_secs: 123,
stream_idle_timeout_secs: 123,
upstream_model: '<string>'
})
};
fetch('https://{host}/api/llm-gateway/admin/model-mappings/{id}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://{host}/api/llm-gateway/admin/model-mappings/{id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => json_encode([
'bare_alias' => true,
'change_note' => '<string>',
'cooldown_429_default_secs' => 123,
'cooldown_base_secs' => 123,
'cooldown_failure_threshold' => 123,
'cooldown_max_secs' => 123,
'cooldown_overloaded_secs' => 123,
'cooldown_window_secs' => 123,
'enabled' => true,
'model_alias' => '<string>',
'priority' => 123,
'request_timeout_secs' => 123,
'retry_on_429_count' => 123,
'retry_on_429_max_wait_secs' => 123,
'stream_idle_timeout_secs' => 123,
'upstream_model' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://{host}/api/llm-gateway/admin/model-mappings/{id}"
payload := strings.NewReader("{\n \"bare_alias\": true,\n \"change_note\": \"<string>\",\n \"cooldown_429_default_secs\": 123,\n \"cooldown_base_secs\": 123,\n \"cooldown_failure_threshold\": 123,\n \"cooldown_max_secs\": 123,\n \"cooldown_overloaded_secs\": 123,\n \"cooldown_window_secs\": 123,\n \"enabled\": true,\n \"model_alias\": \"<string>\",\n \"priority\": 123,\n \"request_timeout_secs\": 123,\n \"retry_on_429_count\": 123,\n \"retry_on_429_max_wait_secs\": 123,\n \"stream_idle_timeout_secs\": 123,\n \"upstream_model\": \"<string>\"\n}")
req, _ := http.NewRequest("PUT", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.put("https://{host}/api/llm-gateway/admin/model-mappings/{id}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"bare_alias\": true,\n \"change_note\": \"<string>\",\n \"cooldown_429_default_secs\": 123,\n \"cooldown_base_secs\": 123,\n \"cooldown_failure_threshold\": 123,\n \"cooldown_max_secs\": 123,\n \"cooldown_overloaded_secs\": 123,\n \"cooldown_window_secs\": 123,\n \"enabled\": true,\n \"model_alias\": \"<string>\",\n \"priority\": 123,\n \"request_timeout_secs\": 123,\n \"retry_on_429_count\": 123,\n \"retry_on_429_max_wait_secs\": 123,\n \"stream_idle_timeout_secs\": 123,\n \"upstream_model\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://{host}/api/llm-gateway/admin/model-mappings/{id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"bare_alias\": true,\n \"change_note\": \"<string>\",\n \"cooldown_429_default_secs\": 123,\n \"cooldown_base_secs\": 123,\n \"cooldown_failure_threshold\": 123,\n \"cooldown_max_secs\": 123,\n \"cooldown_overloaded_secs\": 123,\n \"cooldown_window_secs\": 123,\n \"enabled\": true,\n \"model_alias\": \"<string>\",\n \"priority\": 123,\n \"request_timeout_secs\": 123,\n \"retry_on_429_count\": 123,\n \"retry_on_429_max_wait_secs\": 123,\n \"stream_idle_timeout_secs\": 123,\n \"upstream_model\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"cooldown_429_default_secs": 123,
"cooldown_base_secs": 123,
"cooldown_failure_threshold": 123,
"cooldown_max_secs": 123,
"cooldown_overloaded_secs": 123,
"cooldown_window_secs": 123,
"bare_alias": true,
"enabled": true,
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"kind": "model",
"model_alias": "<string>",
"priority": 123,
"provider_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"retry_on_429_count": 123,
"retry_on_429_max_wait_secs": 123,
"source": "catalog",
"upstream_model": "<string>",
"created_by_email": "<string>",
"created_by_name": "<string>",
"created_by_user_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"last_change_note": "<string>",
"last_modified_at": "2023-11-07T05:31:56Z",
"last_modified_by_email": "<string>",
"last_modified_by_name": "<string>",
"last_modified_by_user_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"auto_disabled_reason": "catalog_retired",
"request_timeout_secs": 123,
"stream_idle_timeout_secs": 123
}Update a model mapping
When require_pricing_for_mappings is on, an enabled post-update
row must have pricing configured for its upstream_model. Mirrors
the create-path guard so disabled rows can’t be silently re-enabled
(or swapped to an unpriced upstream) without re-validating.
enabled: false is always allowed.
curl --request PUT \
--url https://{host}/api/llm-gateway/admin/model-mappings/{id} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"bare_alias": true,
"change_note": "<string>",
"cooldown_429_default_secs": 123,
"cooldown_base_secs": 123,
"cooldown_failure_threshold": 123,
"cooldown_max_secs": 123,
"cooldown_overloaded_secs": 123,
"cooldown_window_secs": 123,
"enabled": true,
"model_alias": "<string>",
"priority": 123,
"request_timeout_secs": 123,
"retry_on_429_count": 123,
"retry_on_429_max_wait_secs": 123,
"stream_idle_timeout_secs": 123,
"upstream_model": "<string>"
}
'import requests
url = "https://{host}/api/llm-gateway/admin/model-mappings/{id}"
payload = {
"bare_alias": True,
"change_note": "<string>",
"cooldown_429_default_secs": 123,
"cooldown_base_secs": 123,
"cooldown_failure_threshold": 123,
"cooldown_max_secs": 123,
"cooldown_overloaded_secs": 123,
"cooldown_window_secs": 123,
"enabled": True,
"model_alias": "<string>",
"priority": 123,
"request_timeout_secs": 123,
"retry_on_429_count": 123,
"retry_on_429_max_wait_secs": 123,
"stream_idle_timeout_secs": 123,
"upstream_model": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.put(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PUT',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
bare_alias: true,
change_note: '<string>',
cooldown_429_default_secs: 123,
cooldown_base_secs: 123,
cooldown_failure_threshold: 123,
cooldown_max_secs: 123,
cooldown_overloaded_secs: 123,
cooldown_window_secs: 123,
enabled: true,
model_alias: '<string>',
priority: 123,
request_timeout_secs: 123,
retry_on_429_count: 123,
retry_on_429_max_wait_secs: 123,
stream_idle_timeout_secs: 123,
upstream_model: '<string>'
})
};
fetch('https://{host}/api/llm-gateway/admin/model-mappings/{id}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://{host}/api/llm-gateway/admin/model-mappings/{id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => json_encode([
'bare_alias' => true,
'change_note' => '<string>',
'cooldown_429_default_secs' => 123,
'cooldown_base_secs' => 123,
'cooldown_failure_threshold' => 123,
'cooldown_max_secs' => 123,
'cooldown_overloaded_secs' => 123,
'cooldown_window_secs' => 123,
'enabled' => true,
'model_alias' => '<string>',
'priority' => 123,
'request_timeout_secs' => 123,
'retry_on_429_count' => 123,
'retry_on_429_max_wait_secs' => 123,
'stream_idle_timeout_secs' => 123,
'upstream_model' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://{host}/api/llm-gateway/admin/model-mappings/{id}"
payload := strings.NewReader("{\n \"bare_alias\": true,\n \"change_note\": \"<string>\",\n \"cooldown_429_default_secs\": 123,\n \"cooldown_base_secs\": 123,\n \"cooldown_failure_threshold\": 123,\n \"cooldown_max_secs\": 123,\n \"cooldown_overloaded_secs\": 123,\n \"cooldown_window_secs\": 123,\n \"enabled\": true,\n \"model_alias\": \"<string>\",\n \"priority\": 123,\n \"request_timeout_secs\": 123,\n \"retry_on_429_count\": 123,\n \"retry_on_429_max_wait_secs\": 123,\n \"stream_idle_timeout_secs\": 123,\n \"upstream_model\": \"<string>\"\n}")
req, _ := http.NewRequest("PUT", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.put("https://{host}/api/llm-gateway/admin/model-mappings/{id}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"bare_alias\": true,\n \"change_note\": \"<string>\",\n \"cooldown_429_default_secs\": 123,\n \"cooldown_base_secs\": 123,\n \"cooldown_failure_threshold\": 123,\n \"cooldown_max_secs\": 123,\n \"cooldown_overloaded_secs\": 123,\n \"cooldown_window_secs\": 123,\n \"enabled\": true,\n \"model_alias\": \"<string>\",\n \"priority\": 123,\n \"request_timeout_secs\": 123,\n \"retry_on_429_count\": 123,\n \"retry_on_429_max_wait_secs\": 123,\n \"stream_idle_timeout_secs\": 123,\n \"upstream_model\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://{host}/api/llm-gateway/admin/model-mappings/{id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"bare_alias\": true,\n \"change_note\": \"<string>\",\n \"cooldown_429_default_secs\": 123,\n \"cooldown_base_secs\": 123,\n \"cooldown_failure_threshold\": 123,\n \"cooldown_max_secs\": 123,\n \"cooldown_overloaded_secs\": 123,\n \"cooldown_window_secs\": 123,\n \"enabled\": true,\n \"model_alias\": \"<string>\",\n \"priority\": 123,\n \"request_timeout_secs\": 123,\n \"retry_on_429_count\": 123,\n \"retry_on_429_max_wait_secs\": 123,\n \"stream_idle_timeout_secs\": 123,\n \"upstream_model\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"cooldown_429_default_secs": 123,
"cooldown_base_secs": 123,
"cooldown_failure_threshold": 123,
"cooldown_max_secs": 123,
"cooldown_overloaded_secs": 123,
"cooldown_window_secs": 123,
"bare_alias": true,
"enabled": true,
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"kind": "model",
"model_alias": "<string>",
"priority": 123,
"provider_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"retry_on_429_count": 123,
"retry_on_429_max_wait_secs": 123,
"source": "catalog",
"upstream_model": "<string>",
"created_by_email": "<string>",
"created_by_name": "<string>",
"created_by_user_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"last_change_note": "<string>",
"last_modified_at": "2023-11-07T05:31:56Z",
"last_modified_by_email": "<string>",
"last_modified_by_name": "<string>",
"last_modified_by_user_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"auto_disabled_reason": "catalog_retired",
"request_timeout_secs": 123,
"stream_idle_timeout_secs": 123
}Authorizations
A Barndoor API key, sent as Authorization: Bearer <key>. Keys are prefixed bdai_ and are opaque — do not attempt to decode one. Interchangeable with an OAuth 2.0 access token.
Path Parameters
Identifier, a UUID
Body
Optional free-text note explaining why this edit was made (BCP-3998).
Deliberately not tri-state like the timeout overrides above: omitting it clears the previous note, because the note describes the latest change and an unrelated later edit must not inherit an explanation written for a different one.
Cooldown on an upstream 429 that carries no usable Retry-After
(a Retry-After sets the window instead). Range 1-3600, and at most
cooldown_max_secs; default 30.
Omitted (or null) keeps the stored value.
First cooldown window once the threshold trips, doubled on each failed
recovery probe up to cooldown_max_secs. Range 1-3600, and at most
cooldown_max_secs; default 30.
Omitted (or null) keeps the stored value.
Gateway failures (5xx, timeouts, connection errors, managed-key 401/403)
within cooldown_window_secs that cool the route. 0 disables every
cooldown of the shared route (rolling failures, 429 and 529); per-user
credential cooldowns on passthrough routes still apply. Range 0-100;
default 10.
Omitted (or null) keeps the stored value.
Cap on every cooldown window: doubling, 429 and 529. Range 1-86400; default 300.
Omitted (or null) keeps the stored value.
Flat cooldown on an upstream 529 "overloaded" with no usable
Retry-After; it does not spend the failure budget. 0 counts a 529 as
an ordinary gateway failure instead. Range 0-3600, and at most
cooldown_max_secs unless 0; default 10.
Omitted (or null) keeps the stored value.
Width of the rolling failure window, seconds. Range 1-3600; default 60.
Omitted (or null) keeps the stored value.
None (omitted) leaves the column unchanged. Some(None) (JSON
null) clears the override. Some(Some(secs)) sets the non-streaming
total-request timeout; the range is fixed platform policy
(TOTAL_REQUEST_TIMEOUT_MIN_SECS..=TOTAL_REQUEST_TIMEOUT_MAX_SECS, see
[crate::proxy::timeouts]).
None (omitted) leaves the column unchanged. Some(None) (JSON
null) clears the override. Some(Some(secs)) sets the streaming
per-chunk timeout; the range is fixed platform policy
(STREAM_IDLE_TIMEOUT_MIN_SECS..=STREAM_IDLE_TIMEOUT_MAX_SECS, see
[crate::proxy::timeouts]).
Response
The requested record
Who created and last changed this route, and why (BCP-3998).
Read with try_get, so the reads that don't select the columns come
back unattributed rather than failing.
Cooldown on a 429 with no usable Retry-After, seconds.
First cooldown window, doubled on each failed recovery probe up to
cooldown_max_secs.
Gateway failures within cooldown_window_secs that cool the route.
0 disables every cooldown of the shared route.
Cap on every cooldown window.
Flat cooldown on a 529 "overloaded" with no usable Retry-After,
seconds. 0 counts a 529 as an ordinary gateway failure instead.
Width of the rolling failure window, seconds.
When true the row participates in bare-name resolution
(POST /v1/chat/completions {"model": "<alias>"}). Route targets are
true; enablements (kind = model) are always false and are
addressable solely via the explicit <provider>/<upstream> form.
Whether this row is the model's enablement or a route target — see
[MappingKind]. Never inferred from model_alias == upstream_model:
a route may carry its upstream's name (migration V84).
model, route Same-route retries on upstream 429. 0 means immediate failover.
Cap on honoring upstream Retry-After header. 0 means use a small
built-in default and ignore the header value.
Whether the model was picked from the provider catalog or typed in by
an admin. Stamped once at create time and never recomputed — see
[ModelSource] and migration V61.
catalog, custom Optional free text explaining why the last change was made ("Raised for the Q3 batch job", "Vendor deprecated the old endpoint").
Admin-authored and rendered in the portal, so it is untrusted input: length-capped here, escaped on render.
When the change named by last_modified_by_* happened.
Its own column rather than the row's updated_at, because updated_at
is bumped by writes nobody is attributed for -- pairing the two would
date a named admin's edit to a background job's clock. See the V75
migration for the full reasoning.
Set when the gateway turned this row off (BCP-3811's full sync
retiring a model the catalog dropped). None means a human decided
the enabled flag, and the reconciler will not touch it in either
direction — see migration V63. Any admin write to enabled clears this.
Clearing it is not a durable claim on the row, and it is easy to read
it that way. It protects a human's disable — the re-enable branch
requires the marker to be present, so a row a human turned off is never
turned back on. It cannot protect a human's re-enable: a cleared
marker is exactly what makes the row eligible for retirement again, so
under full a model the catalog still does not list is re-disabled on
the next tick. That is full's contract ("enabled set tracks the
catalog"), not a bug — see
model_sync::tests::re_enabling_a_dropped_model_is_retired_again_on_the_next_tick.
catalog_retired Per-mapping (route-tier / model-tier) total-request timeout for
non-streaming requests (streaming responses have no total timeout).
Defaults to TOTAL_REQUEST_TIMEOUT_DEFAULT_SECS at insert. On a route
target this is the route tier; on the enablement (kind = model)
it is the model tier consulted by the resolver for every route
target of that model. The range is fixed platform policy
(TOTAL_REQUEST_TIMEOUT_MIN_SECS..=TOTAL_REQUEST_TIMEOUT_MAX_SECS),
enforced by the column CHECK.
Per-mapping stream-idle (per-chunk) timeout for streaming responses.
Defaults to STREAM_IDLE_TIMEOUT_DEFAULT_SECS at insert; enforced via
tokio::time::timeout around each upstream SSE/eventstream chunk
read. The range is fixed platform policy
(STREAM_IDLE_TIMEOUT_MIN_SECS..=STREAM_IDLE_TIMEOUT_MAX_SECS),
enforced by the column CHECK.