> ## Documentation index
> Fetch the complete documentation index at: https://rolter-ai-rolter.docs7.io/llms.txt
> Ask it with GET https://rolter-ai-rolter.docs7.io/ask?q=<keywords or question>. Short keywords rank best.
> Use these to discover all available pages before exploring further.

# Client Settings: Base URL and Upstream Headers

> The base URL Rolter advertises to clients, which inbound headers reach the provider, which headers Rolter adds itself, and the header carrying the request id.

Client settings cover the seam between your callers and your providers: what address clients dial, which of their headers survive the hop, and what Rolter attaches on the way out.

Edit them from **Settings → Client Settings** in the dashboard, or through the management API. Changes reach every gateway through the config snapshot, with no restart.

---

## Base URL

The address clients point their SDK at: the gateway's own root, such as `https://gateway.example.com`. Set it when the gateway sits behind a load balancer or on a custom domain. The dashboard builds every connection snippet from it (the request on the **Getting started** card, the Playground's **Copy as code** and the example request on this screen), so what you copy is what a caller can reach.

Enter it without `/v1`, since the snippets add the path themselves. A trailing `/v1`, the way the OpenAI SDKs write their base URL, is dropped rather than doubled.

Leave it empty and the dashboard shows no connection snippet: it asks you to save a base URL instead. The dashboard's own `/gw` proxy is never offered as an address for your clients. It requires a dashboard session as `Authorization: Bearer <session token>` (or the admin token; a control plane in open mode needs none), which a client outside the dashboard does not have, and each call passes through the control plane on its way to the gateway. The Playground and the gateway status pill are the proxy's callers: they send the session, and the virtual key goes in an `x-rolter-gateway-key` header, which the proxy forwards to the gateway as `Authorization: Bearer <key>`.

Every signed-in user's snippets use the saved base URL, whatever their role. Only a superadmin can read client settings, but the address is not a secret (it is what clients already dial), so `GET /api/v1/auth/me` returns it as `gateway_base_url` (`null` when none is saved) for any session.

This value is advisory: the gateway never reads it, and setting it does not change what the gateway listens on.

---

## Forwarded request headers

An allowlist of inbound client headers passed through to the upstream provider. Anything not on the list is dropped at the gateway — that is the default, and an empty list means Rolter sends exactly what it sent before this setting existed.

Trace-context headers (`traceparent`, `tracestate`, `b3`) propagate whether or not they are listed, so distributed tracing cannot be broken by editing this list.

Some headers can never be forwarded, because forwarding them would hand a caller the gateway's own credentials or corrupt the upstream request: `authorization`, `x-api-key`, `api-key`, `host`, `content-length`, `content-type`, `transfer-encoding`, `connection` and `cookie`. Listing one is rejected.

### `anthropic-*` headers forward by prefix

Every inbound header beginning with `anthropic-` is forwarded to an Anthropic-dialect upstream **unchanged, without an allowlist entry** — including values Rolter has never seen.

Anthropic extends its protocol through headers: a capability pairs an `anthropic-beta` value with body fields, and the pair travels together. A closed allowlist against a vendor that ships new capabilities _as new header values_ means every new beta breaks until someone adds it by name, and it does not fail gracefully — stripping the header while the body passes returns a hard `400` (`Extra inputs are not permitted`), while capabilities such as extended context simply never take effect.

<Note>
  The match requires the separator, so a header merely starting with those letters (`anthropicfoo`) is not in the namespace. These headers are also dropped again for a provider that does not speak the Anthropic dialect, so an OpenAI-shaped upstream never receives them.
</Note>

`anthropic-version` is the one value Rolter resolves rather than copies: the client's wins when it sends one, and the deployment's configured `anthropic_version` is the fallback for requests translated from the OpenAI dialect, where the client stated none. Exactly one value is sent.

---

## Injected upstream headers

Static name/value pairs Rolter adds to every upstream request — a partner id, a routing tag, a billing dimension your provider expects.

Injected headers are applied _before_ trace context, so a trace header of the same name still wins. Values are treated as deployment credential material: they reach the gateway through the snapshot, but they are never written to the audit log. The log records which header **names** changed, not what they were set to.

The same reserved list applies here.

---

## Request ID header

The header carrying the end-to-end request id. Rolter reuses the caller's value when one is present, mints a UUID when it is missing or blank, and echoes it on the response so a client can find its own call in the logs. The same id is the `request_id` of the call's row in **LLM Logs** and the MCP tool-call log, and it appears on the gateway's log line for a failed request.

Default is `x-request-id`. Set another name, such as `x-correlation-id`, when your clients already send their id under it. The gateway then reads and echoes only that header: an `x-request-id` a caller sends is ignored, and none is added to the response. A change reaches every gateway with the next snapshot and applies from the next request, without a restart.

The id is not sent to the provider unless you list the same header under **Forwarded request headers**. A listed header carries the caller's id, or the one Rolter minted, so the provider's logs and yours share it. An MCP server receives it either way, because the MCP proxy passes the caller's headers through.

The name must be a valid header name. Besides the reserved names above, it cannot be a header that carries a credential, session state or message framing, because the id is copied into the logs and written onto the response: `x-goog-api-key`, `proxy-authorization`, `set-cookie`, `expect`, `te`, `trailer`, `upgrade`, `keep-alive` and the other hop-by-hop headers are refused as well.

---

## API

Both endpoints are **superadmin only**: no [deployment role](/security/rbac#deployment-roles) reaches them. Every update is audited.

```bash
curl -H "Authorization: Bearer $ROLTER_ADMIN_TOKEN" \
  https://control.example.com/api/v1/client-settings

curl -X PUT -H "Authorization: Bearer $ROLTER_ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
        "public_base_url": "https://gateway.example.com",
        "forwarded_headers": ["x-tenant-id"],
        "injected_headers": {"x-partner-id": "acme"},
        "request_id_header": "x-request-id"
      }' \
  https://control.example.com/api/v1/client-settings
```

The read response also carries `always_propagated`, `reserved` and `request_id_refused` (every name refused as the request id header), so a client does not have to hard-code any of the lists. The dashboard checks the Request ID header field against `request_id_refused` before you save.

On the **Client Settings** screen each problem is shown under the field it is about, and the footer counts them. **Save changes** stays off until the form differs from what is saved, the footer says when there are unsaved changes, and leaving the screen with unsaved changes asks first. A saved change reaches the gateways on their next config poll, without a restart.
