Choose where a request runs
Send `provider` with a request to keep it in the EU, in one country, or on the Inference Providers you name — on every surface, failover included.
Your workspace policy sets the rule for every request. Sometimes one request needs a tighter rule. An example: your own customer in Germany needs EU compute, and a customer elsewhere does not. The provider object sets that rule for one request.
The three fields
| Field | Value | Effect |
|---|---|---|
runs_in | "eu", "eea", a country code ("NO"), or a list of country codes (["NO", "SE"]) | Only endpoints that run there serve |
only | Inference Provider slugs | Only these providers' endpoints serve |
ignore | Inference Provider slugs | These providers' endpoints never serve |
region is another name for runs_in, with the same meaning. It is not a provider region code. To pick one of a provider's regions, put it in the model id: creator/model/provider:<code>.
Use the slugs that GET /v1/models/{model}/endpoints lists for the model.
What it guarantees
- It only narrows. The request runs inside your workspace policy and inside the filter. It can never reach an endpoint that your workspace policy excludes.
- It applies to the endpoint, not the model.
only: ["ovhcloud-ai-endpoints"]uses OVHcloud's endpoint, whichever company is listed as the model's provider. - Short names work too. Some providers also take a short name, for example
ovhforovhcloud-ai-endpoints. It is the same short name a pin takes (baai/bge-m3/ovh), and the request log shows the full slug. - Failover stays inside it. If the first endpoint fails, Stav tries the model's next endpoint that passes the filter. If no endpoint passes, you get Stav's own error. The request is never sent outside the filter to rescue it.
- Nothing is sent when nothing passes. You get a refusal and the request does not reach any provider.
Chat
from openai import OpenAI
client = OpenAI(base_url="https://api.stav.ai/v1", api_key="sk_stav_live_...")
resp = client.chat.completions.create(
model="mistralai/mistral-small-4-119b-2603",
messages=[{"role": "user", "content": "Summarise this contract clause."}],
extra_body={"provider": {"runs_in": "eu"}},
)
This works the same with model="auto" and with a Router. The Smart Router only scores endpoints inside the filter.
Messages (Anthropic SDK)
import anthropic
client = anthropic.Anthropic(base_url="https://api.stav.ai", api_key="sk_stav_live_...")
msg = client.messages.create(
model="mistralai/mistral-small-4-119b-2603",
max_tokens=512,
messages=[{"role": "user", "content": "Hei!"}],
extra_body={"provider": {"runs_in": "NO"}},
)
Most Claude endpoints are global. global is not in any region, so runs_in: "eu" refuses them until an EU endpoint for that model exists.
Responses
resp = client.responses.create(
model="openai/gpt-5",
input="Hello",
extra_body={"provider": {"ignore": ["azure"]}},
)
Stav takes provider off the body. It is never sent to the provider.
Embeddings
emb = client.embeddings.create(
model="baai/bge-m3",
input=["Hovedstaden i Norge er Oslo."],
extra_body={"provider": {"runs_in": "eu"}},
)
Rerank
curl https://api.stav.ai/v1/rerank \
-H "Authorization: Bearer $STAV_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "baai/bge-reranker-v2-m3", "query": "Where is Stavanger?",
"documents": ["Stavanger is in Norway.", "Paris is in France."],
"provider": {"runs_in": "eu"}}'
Speech and decisions
/v1/audio/speech and /v1/decisions take the same provider object in the JSON body.
Transcriptions
/v1/audio/transcriptions uses a multipart form. Send provider as one form field that holds a JSON object:
curl https://api.stav.ai/v1/audio/transcriptions \
-H "Authorization: Bearer $STAV_API_KEY" \
-F file=@meeting.wav \
-F model=mistralai/voxtral-mini-2602 \
-F 'provider={"runs_in": "eu"}'
Fields that apply on chat only
max_price and quantizations filter the endpoints of a chat request, like the fields above: max_price is a price per million tokens, and embeddings, rerank, audio and decisions are not all billed that way. sort orders a named model's endpoints on the chat surfaces only (see Routing hints). Stav refuses all three there with 400 unsupported_parameter.
When the request is refused
| You get | Because | What to do |
|---|---|---|
400 invalid_value, param: provider.runs_in | The value is not eu, eea or a country code, or it mixes a region and countries in one list | Send one region, or a list of countries |
400 invalid_value, param: provider.only (or ignore) | A slug is not an Inference Provider | Use a slug from GET /v1/models/{model}/endpoints |
400 unsupported_parameter, param: provider.<field> | The field is not supported on this surface | Remove it |
400 invalid_value, param: provider | The model id pins a provider that the filter excludes | Remove the provider from the model id, or change the filter |
403 endpoint_not_admissible | No endpoint of the model passes the filter together with your workspace policy | Change the filter, or pick a model with an endpoint inside it |
403 provider_blocked | Every endpoint of the model is on a provider that ignore names | Remove it from ignore |
Check it in the response
Every response says where it ran, so your code can check a runs_in: "eu" call with no second request.
| Where | Value |
|---|---|
X-Stav-Compute-Region header | eu, eea, us, global or unknown. Always sent |
X-Stav-Compute-Countries header | Every country the request may have run in, e.g. FI,FR. Sent when a country is known |
usage.compute_region, usage.compute_countries in the body | The same two values. On a stream, on the final frame that carries usage |
global means the Inference Provider pins no residency. unknown means Stav holds no fact about where the endpoint runs. They are two different values.
A call ran in the EU when the region is eu, or when every country is an EU state. Speech answers with audio, so it has the headers only. A transcription in text, srt or vtt has the headers only too.
See what happened
Every request with a filter shows it in Your workspace → Monitor → Request log. Open the request to see the filter, the endpoint that served, and the country it ran in.
Next steps
- Enforce a sovereignty floor — the workspace rule this narrows
- Sovereignty
- Routing hints