xAI Grok Provider
Overview
Section titled “Overview”shunt ships a built-in xai provider that translates Anthropic Messages to xAI’s OpenAI-Responses-shaped API at https://api.x.ai/v1/responses. It is reachable two ways: an API key (XAI_API_KEY, the default) or a subscription OAuth login that reuses a SuperGrok / X Premium+ plan with no separate API billing src/config.rs:300-311 docs/m6-xai-provider.md.
| Path | Credential | Cost model | Setup |
|---|---|---|---|
| API key (default) | XAI_API_KEY env var |
Pay-per-token xAI API billing | export XAI_API_KEY=... + routes |
| Subscription OAuth | ~/.shunt/xai-auth.json, written by shunt login xai |
Included in SuperGrok / X Premium+ | auth = "xai_oauth" + one device-code login |
API key setup
Section titled “API key setup”The built-in provider already defines kind = "responses", base_url = "https://api.x.ai/v1", and api_key_env = "XAI_API_KEY", so a minimal shunt.toml only adds routes src/config.rs:295-311:
[[routes]]model = "grok-build-0.1" # flagship coding modelprovider = "xai"
[[routes]]model = "grok-4.3"provider = "xai"export XAI_API_KEY=xai-...shunt runSubscription OAuth setup
Section titled “Subscription OAuth setup”Flip the provider’s auth mode and log in once with the RFC 8628 device-code flow:
[providers.xai]auth = "xai_oauth"shunt login xai # prints a verification URL + short code; approve in any browsershunt login xai requests a device code from https://auth.x.ai/oauth2/device/code, prints the verification URL, and polls the token endpoint until the login is approved src/auth/xai_login.rs:52-100. Because there is no loopback callback server, the flow works over SSH, in containers, and on headless VPS hosts. Credentials are written atomically at 0600 to ~/.shunt/xai-auth.json (override with SHUNT_XAI_AUTH_FILE) src/auth/mod.rs:119-129.
Token lifecycle
Section titled “Token lifecycle”| Behavior | Detail | Source |
|---|---|---|
| Expiry | Access-token JWT exp claim, 5-minute buffer |
src/auth/xai_auth.rs:87-121 |
| Refresh-token rotation | xAI rotates the refresh token on every refresh; shunt persists the rotated pair | src/auth/xai_auth.rs:106-120 |
| Concurrent refresh | Process-wide single-flight mutex; waiters re-read the winner’s rotated pair | src/auth/xai_auth.rs:43-48 |
Refresh 403 |
Subscription tier is not entitled to API access — re-login will not help; the error points at the XAI_API_KEY path |
src/auth/xai_auth.rs:233-246 |
Refresh 400/401 |
Consumed or invalid refresh token — run shunt login xai again |
src/auth/xai_auth.rs:233-246 |
Safety and request shaping
Section titled “Safety and request shaping”- Bearer-leak guard: a provider with
auth = "xai_oauth"must bekind = "responses", use an httpsbase_url, and stay on anx.aihost; anything else fails validation at boot, so the subscription bearer can never be sent off-origin or over plaintext src/config.rs:424-435. - Reasoning is opt-in: several grok models reject
reasoning.effortwith a 400, so shunt sends areasoningobject only when aneffortwas explicitly chosen — configured on the route/provider, or sent per-request by the client (output_config.effort) src/model/responses_request.rs:34-51. - The xAI dialect also omits the
textobject and theOpenAI-Betaheader, and always sendsstore: false; detection is table-driven from the provider’s auth mode and base-URL host, never a hardcoded provider name src/config.rs:489-505 src/adapters/responses.rs:214-240.
Related Pages
Section titled “Related Pages”| Page | Relation |
|---|---|
| Configuration | Provider table keys and route syntax |
| Authentication | All auth modes side by side |
| Adapters and Translation | Responses translation internals |