# Run Goose on Router One with OPENAI_HOST and exact model IDs

> Markdown mirror of https://router.one/integrations/goose for AI assistants and crawlers. Router One is an OpenAI-compatible LLM API gateway.
> Last updated: 2026-09-19

Goose is an open-source AI agent, part of the Agentic AI Foundation at the Linux Foundation and still shipped as block-goose on Homebrew, that runs as a desktop app or a CLI and executes tools through extensions on your machine. Its built-in OpenAI provider accepts any OpenAI-compatible endpoint: OPENAI_HOST is the host root and OPENAI_BASE_PATH, which defaults to v1/chat/completions, is appended to it. Set the host to Router One and every model turn becomes one request on the gateway with a cost, latency and status trace, while GOOSE_MODEL selects any chat model in the catalog by its exact ID. This guide covers the environment-variable setup, the matching goose configure prompts and Desktop fields, the custom-provider JSON alternative, the rule that routes gpt-5-family names to /v1/responses, and how to reconcile a tool-calling session against Dashboard → Logs.

## Install Goose and create a dedicated key

Install the CLI with the official script or with Homebrew (brew install block-goose-cli); the Desktop app is a download or brew install --cask block-goose. The script normally ends in an interactive goose configure, and CONFIGURE=false skips that step, which suits this guide: the provider is configured through environment variables below, and goose configure can persist the same values later. Then create a Router One key for this machine with a maxSpend cap, and copy the exact ID of one chat model from /models; Goose sends that string unchanged in the model field. goose --version confirms the install. The paths and defaults below were checked against the goose-docs.ai pages and the v1.50.1 source.

`terminal`

```bash
# CLI on macOS/Linux: official install script; CONFIGURE=false skips the interactive setup
curl -fsSL https://github.com/aaif-goose/goose/releases/download/stable/download_cli.sh | CONFIGURE=false bash
goose --version
# Homebrew instead: brew install block-goose-cli   (Desktop: brew install --cask block-goose)
```

## Configure Goose to use the Router One base URL

Save the block as goose-env.sh, replace the two placeholders, run source goose-env.sh, then start goose session. GOOSE_PROVIDER=openai selects the built-in OpenAI provider, whose id is openai. GOOSE_MODEL is the catalog ID, copied character for character. OPENAI_API_KEY is sent as the Bearer token; a key in the environment takes precedence over one stored by goose configure, and a key written into config.yaml is ignored and surfaces as an authentication failure. OPENAI_HOST is the host root with no /v1: the provider joins OPENAI_BASE_PATH onto it with a single slash, so with the default path v1/chat/completions the request goes to https://api.router.one/v1/chat/completions; a trailing slash on the host is harmless, while a host that already ends in /v1 produces /v1/v1/chat/completions. OPENAI_BASE_PATH therefore stays unset. One rule from the provider source matters when choosing a model: on the default base path Goose picks the endpoint per model name. IDs that look like OpenAI's gpt-5, gpt-6 or o-series names, matched at the start of the ID or right after a / or a hyphen, so openai/gpt-5.5 qualifies, are sent to /v1/responses; every other name goes to /v1/chat/completions. Router One serves /v1/responses natively for the listed GPT-family IDs, so both paths resolve, and Claude, Gemini, Grok and DeepSeek IDs stay on Chat Completions. goose info -v prints the effective provider, model and host before the first request:

`goose-env.sh`

```bash
# Goose built-in OpenAI provider -> Router One (macOS/Linux). source this file, then run: goose session
export GOOSE_PROVIDER="openai"
export GOOSE_MODEL="<exact-model-id-from-/models>"
export OPENAI_API_KEY="sk-your-router-one-key"
export OPENAI_HOST="https://api.router.one"   # host root only: no /v1, no path
# OPENAI_BASE_PATH stays at its default, v1/chat/completions.
# Goose joins it to OPENAI_HOST -> POST /v1/chat/completions
# (gpt-5, gpt-6 and o-series names are sent to /v1/responses instead).
```

## Which value goes where, and how to verify it

The same values can be entered through goose configure (Configure Providers → OpenAI prompts for the key, then for OPENAI_HOST with https://api.openai.com as the default and for OPENAI_BASE_PATH with v1/chat/completions as the default) or in goose Desktop (Settings → Models → Configure providers → OpenAI: API Key and Host URL). Whichever surface you use, the request that reaches the gateway is the same:

| Goose field | Value | What to verify |
| --- | --- | --- |
| GOOSE_PROVIDER (env), active_provider in config.yaml, or the provider chosen in goose configure / Desktop | openai | goose info -v lists the provider; a custom provider uses its own name instead (custom_… below) |
| GOOSE_MODEL (env), providers.openai.model in config.yaml, or Desktop Switch models → Use custom model | <exact-model-id-from-/models> | The model in each Logs trace matches /models character for character; goose configure fetches its picker list from GET /v1/models on the gateway, and GOOSE_MODEL set directly covers any ID the picker does not show |
| OPENAI_HOST (env or the goose configure prompt), Desktop Host URL | https://api.router.one | No /v1 and no path; a 404 on the first turn means the path doubled to /v1/v1/chat/completions |
| OPENAI_BASE_PATH | Unset, so the default v1/chat/completions applies | The Logs trace shows POST /v1/chat/completions, or POST /v1/responses for gpt-5, gpt-6 and o-series names; a value containing responses would force every model onto /v1/responses |
| OPENAI_API_KEY (env, or stored in the keyring by goose configure) | A Router One key created for this machine, with maxSpend | goose configure reports OPENAI_API_KEY is set via environment variable; the first trace appears under that key in Logs; a key pasted into config.yaml is ignored |
| OPENAI_ORGANIZATION, OPENAI_PROJECT, OPENAI_CUSTOM_HEADERS, OPENAI_TIMEOUT | Leave unset (the timeout default is 600 s) | The gateway authenticates with the Bearer key alone; these add headers or change the timeout, never the path or the model |
| GOOSE_CONTEXT_LIMIT | The context window from the model page, as a token count | Goose falls back to 128,000 for names it does not recognize; the value drives its token-usage display and compaction, not what the gateway accepts |

## Alternative: a custom provider JSON file

To keep Router One separate from the OpenAI provider, with its own name in the picker, its own key and a fixed model list, define a custom provider. goose configure → Custom Providers → Add A Custom Provider and Desktop's Add Custom Provider (Provider Type OpenAI Compatible, Display Name, API URL, API Key, Available Models, Streaming Support) write the same JSON file into the custom_providers directory: ~/.config/goose/custom_providers/ on macOS and Linux, %APPDATA%\Block\goose\config\custom_providers\ on Windows, one <name>.json per provider. base_url takes the full Chat Completions URL, as in the official example; Goose splits it into the host and the request path, so each turn is again POST /v1/chat/completions, and the same model-name rule sends gpt-5, gpt-6 and o-series IDs to /v1/responses. models lists the IDs you want offered, each with context_limit set to the context window from the model page, never an output-token figure; Goose also queries GET /v1/models on the gateway and lists what it returns. api_key_env names the variable that holds the key: export it before starting Goose, or enter the key in Desktop so it is stored in the keychain. Select the provider by setting GOOSE_PROVIDER to the name field:

`~/.config/goose/custom_providers/custom_router_one.json`

```json
{
  "name": "custom_router_one",
  "engine": "openai",
  "display_name": "Router One",
  "description": "Router One unified LLM API gateway",
  "api_key_env": "CUSTOM_ROUTER_ONE_API_KEY",
  "base_url": "https://api.router.one/v1/chat/completions",
  "models": [
    { "name": "<exact-model-id-from-/models>", "context_limit": <context-window-from-the-model-page> }
  ],
  "supports_streaming": true,
  "requires_auth": true
}
```

## Budget a session and reconcile its turns

An agent session is many model requests. Goose calls the model once per turn and again after every tool result, and its provider layer retries rate-limit, server and network errors up to three times with exponential backoff (1 s, doubling, capped at 30 s), so each attempt that reaches the gateway is its own request in Dashboard → Logs with its own request_id, tokens, cost and status. Give each machine running Goose a dedicated key with maxSpend: a session that reaches the cap gets HTTP 402 on its next turn while the wallet and other keys stay untouched. Cap the loop inside Goose as well: GOOSE_MAX_TURNS (default 1000) or --max-turns on goose session and goose run, and --max-tool-repetitions for identical repeated calls. Three records exist and only one is money. Logs is the charge per request. Goose's own llm_request.*.jsonl files, the ten most recent under ~/.local/state/goose/logs/, hold the model configuration, request payload, response and token usage, which is the quickest way to see which model string left the machine. GOOSE_CLI_SHOW_COST (default false) prints an estimated cost that is not the gateway's charge. Reconcile by time, model and token counts, and keep the request_id from Logs when reporting a failure. The developer extension, MCP servers, sessions, permissions (GOOSE_MODE) and compaction all run in Goose; Router One records the model calls and nothing else.

## Which model ID should Goose send?

Copy the exact model ID from /models, preserving case, hyphens, and version suffixes; do not substitute a display name. Open its detail page and match the supported API endpoints, context window, and capabilities such as tool calling to the provider and features selected in Goose. A catalog listing does not mean the client can use every feature of that model. Give each tool a dedicated API key with a maxSpend cap.

## Which API protocol is Goose using?

OpenAI-compatible describes an interface format; it does not make Chat Completions (/v1/chat/completions), Responses (/v1/responses), and Anthropic Messages (/v1/messages) interchangeable. Check the installed client version, provider configuration, and actual request path against the model detail page and API compatibility fact sheet. A successful plain-text chat does not establish support for hosted tools, conversation state, or file-editing features.

## Verify the Goose call in your request trace

Send a simple text request from Goose, then match its trace in Dashboard → Logs by time, model, and request_id: tokens, cost, latency, and status. Next, test streaming, tool calls, and multi-turn history separately. For failures, retain the actual request path, full error message, and request_id. If there is no matching log, check client configuration and connectivity before attributing the error to the gateway or upstream.

## FAQ

### Goose returns 404 on the very first turn after I set OPENAI_HOST. What is wrong?

Almost always the path doubled. OPENAI_HOST must be the host root, https://api.router.one, because Goose appends OPENAI_BASE_PATH to it, v1/chat/completions by default, with a single slash between them. A host of https://api.router.one/v1 therefore requests /v1/v1/chat/completions, and the full endpoint URL in that field makes the path longer still; both answer 404 not_found before any model runs. The Goose docs say the same for proxies: set OPENAI_HOST to the root with no trailing path, and read a 404 as a wrong base path. On Router One the default base path is already right, so fix the host and leave OPENAI_BASE_PATH unset; a base path containing responses would send every model, Claude and Gemini IDs included, to /v1/responses, where any ID outside the GPT and DeepSeek families gets 400 model '<id>' must be called via … before a model runs. Also look for a stale export: an OPENAI_HOST in the environment overrides both the value goose configure saved and OPENAI_BASE_URL, so the host shown by goose info -v is the one that counts. On the custom-provider route Goose derives the request path from base_url, completing a bare host or a /v1 base to /v1/chat/completions, so a 404 there points at a typo in the URL itself.

### Which setting wins: goose configure, config.yaml, or the environment?

Environment variables, then the config file, then defaults; that is the documented order. GOOSE_PROVIDER and GOOSE_MODEL exported in the shell override active_provider and providers.<id>.model in config.yaml for that process, goose run --provider and --model override the environment for one run, and /model switches the model inside a running session. For the host, the provider source reads OPENAI_HOST from the environment first, then OPENAI_BASE_URL (environment or config, with a trailing /v1 stripped), then the OPENAI_HOST saved by goose configure, then https://api.openai.com. The key is the exception to the file: Goose does not read provider API keys from config.yaml; goose configure stores them in the system keyring, or in secrets.yaml where no keyring is available, and OPENAI_API_KEY in the environment takes precedence over the stored value. For goose Desktop, enter the same values in Settings → Models → Configure providers → OpenAI (API Key, Host URL); the docs present that page, not shell variables, as the Desktop configuration surface. The config file lives at ~/.config/goose/config.yaml (Windows: %APPDATA%\Block\goose\config\config.yaml), and goose info -v shows the values in effect.

### Why did Goose send a GPT model to /v1/responses when I never changed the base path?

Because the OpenAI provider chooses the endpoint per model name whenever the base path is the default. Its source matches names that look like OpenAI's reasoning models, gpt-5 and gpt-6 followed by a dot, a hyphen or the end of the name, and o-series names such as o3, at the start of the ID or right after a / or a hyphen, so openai/gpt-5.5 qualifies. Those turns are built as Responses requests to /v1/responses with store set to false. Every other name, gpt-4o and the Claude, Gemini, Grok and DeepSeek IDs included, goes to /v1/chat/completions. Router One serves /v1/responses natively for the currently listed GPT-family and DeepSeek IDs, so nothing needs changing: confirm the path and status of the first turn in Dashboard → Logs, and check that the model page lists POST /v1/responses before relying on Responses-only features. A custom OPENAI_BASE_PATH changes the rule: a path that contains chat/completions but is not the default forces Chat Completions for every model, and a path containing responses forces Responses, so leave it unset unless you want that override.

### Which models can Goose use through the gateway?

Choose a current catalog model that supports both the endpoint and the features Goose uses. Check /models and the model detail page for the exact ID, current rates, and capabilities; a family name such as GPT or Claude is not a compatibility guarantee. Seeing a model in the picker confirms discovery, so verify an actual request too.

### Models are listed, but requests fail with 400 or 404. What should I check?

Record the actual request path and error message, then check the exact model ID. A 400 can indicate invalid parameters, unsupported tools, or a model/endpoint mismatch; a 404 can indicate an incorrect path or missing resource, so it does not by itself establish that a model was retired. If the error says must be called via, use the named endpoint or select a model supported on the current endpoint. Do not add or remove /v1 or /chat/completions across all clients indiscriminately.

### Does this work from Mainland China?

Yes. The gateway is reachable from Mainland China without a VPN, and the configuration is identical to the global setup.

### How do I debug a 401/402/403/429?

Match the request and error message in Dashboard → Logs. For 401, check whether the key was sent and is valid; for 402, check wallet balance and maxSpend; for 403, check key permissions and access restrictions. For 429, distinguish request/token limits from upstream throttling using the error details. Keep the request_id and follow the error-codes reference.

## See also

- All integration guides: https://router.one/integrations
- Debug API errors in Goose: https://router.one/llm-api-error-codes
- API compatibility: endpoints and supported features: https://router.one/facts/api-compatibility.md
- Responses API setup and limits: https://router.one/codex-responses-api
- Mastra setup: https://router.one/integrations/mastra
- DSPy setup: https://router.one/integrations/dspy
- Connection and /v1 path troubleshooting: https://router.one/api-connection-troubleshooting
- Tool-calling API requirements: https://router.one/llm-tool-calling
- One gateway behind every coding tool: https://router.one/use-cases/ai-coding-tools
- Goose: Configure LLM Provider: https://goose-docs.ai/docs/getting-started/providers/
- Goose: Configuration Files: https://goose-docs.ai/docs/guides/config-files/
- Goose: Environment Variables: https://goose-docs.ai/docs/guides/environment-variables/
- What the gateway layer does: https://router.one/llm-api-gateway
- OpenAI-compatible API: https://router.one/openai-compatible-api
- API docs: https://router.one/docs
- Canonical page: https://router.one/integrations/goose
- Models and per-model token rates: https://router.one/models (markdown: https://router.one/models.md)
- Pricing: https://router.one/pricing
- API docs (markdown): https://router.one/docs.md
- Company facts: https://router.one/facts/company.md
