How to Point a Coding Agent at a Different Model Provider
A base URL, a credential, a model ID: that's all any coding agent wants. The four places they go, one test that proves it, and what stops working.
Your coding agent ships pointed at the company that built it, and that's a default, not a requirement. Almost every terminal and IDE agent will talk to a different endpoint if you give it a base URL, a credential and a model ID — and those three values are all any of them want. What changes between tools is where you put them: an environment variable, a TOML table, a JSON block, or four fields on a settings screen. Learn the four shapes and you can point a coding agent at a different model provider in about five minutes, including agents that didn't exist when you read this.
Every coding agent needs the same three values
Every agent that supports a custom provider asks for a base URL, a credential, and a model ID. Get all three right and it works. Get one wrong and the error usually tells you which one, once you know what each failure looks like.
The base URL is shorter than you think
The base URL is the API root, not the endpoint you'd call with curl. Agents append the rest of the path themselves, so the value you paste stops before the part that does the work. One agent appends /v1/messages to whatever you give it, so its base URL ends at the host with no /v1 at all. An OpenAI-format endpoint usually wants the root including /v1, and never the /chat/completions suffix on the end.
Check this before anything else: a base URL with the path already in it produces a confusing 404 on a request that would otherwise have worked. If your endpoint's documentation shows a full example URL, delete the final segment the agent is going to add and keep the rest.
The credential decides which HTTP header your key lands in
The second value is your key, and the variable you put it in isn't cosmetic. On Anthropic-format agents, a key in ANTHROPIC_AUTH_TOKEN is sent as Authorization: Bearer, while a key in ANTHROPIC_API_KEY is sent as x-api-key. Same key, same endpoint, different header — and if the endpoint only reads one of them, the other produces a 401 that looks exactly like an invalid key.
There's a second version of this trap that's easier to hit. If you've used the agent with its original vendor, that vendor's key may still be exported in your shell, and a leftover ANTHROPIC_API_KEY takes precedence over the token you just set. The AVIS setup for Claude Code handles it by setting ANTHROPIC_API_KEY="" explicitly alongside the base URL and the auth token, and names the non-empty leftover as the most common cause of a 401. Running /logout first also clears any cached session from the original vendor.
The third value, the model ID, is the one that reliably breaks: IDs aren't portable between tools, not even between two tools pointed at the same endpoint. That gets its own section below.
Find the shape your agent uses
Agents differ mainly in where the three values live. Four containers are in common use, and identifying yours is faster than hunting for a tutorial written about your specific tool.
| Container | Where it lives | What to watch |
|---|---|---|
| Environment variables | Shell profile, or an env block in the agent's settings file |
A settings-file value typically beats a shell export for the same variable |
| TOML provider table | A config file in the agent's home directory | The provider name must match the table name exactly, or it silently falls back to the default |
| JSON provider block | A project-level or global JSON config | One syntax error can drop the whole provider block without an error message |
| Settings-screen fields | The IDE extension's own settings panel | You may have to declare capabilities the agent can't detect, such as context window size |
Environment variables
This is the most common shape, and the only option for several agents. The pattern is a base URL variable, a credential variable, and usually a model variable. Terminal agents built on the Anthropic format read ANTHROPIC_BASE_URL plus a credential variable; ones built on the OpenAI format often read OPENAI_API_BASE and OPENAI_API_KEY. Two others have their own names for the same three ideas: Copilot CLI takes COPILOT_PROVIDER_BASE_URL, COPILOT_PROVIDER_API_KEY and COPILOT_MODEL with no config file at all, and Gemini CLI takes GOOGLE_GEMINI_BASE_URL alongside its API key variable.
Two practical notes. Order matters when one variable references another — define the variable holding your key before the line that reuses it. And a native installer won't necessarily read a .env file in your project, so put these in a shell profile or the agent's own settings file.
Gemini CLI also shows a failure mode worth knowing in general: a custom base URL is honoured only in API-key authentication mode, not when you're signed in with a Google account or routed through Vertex AI. The variable is set, the agent ignores it, and nothing warns you. If an agent offers several sign-in methods, confirm yours is the one that respects a custom endpoint.
A config-file provider block
Some agents declare providers as structured config instead. Codex CLI uses a TOML table in ~/.codex/config.toml: a top-level model_provider key naming the provider, then a [model_providers.<name>] table holding base_url, env_key — the name of the environment variable your key lives in, so the key itself stays out of the file — and wire_api, which selects the request shape. Setting it to responses targets the /responses endpoint; the alternative value targets /chat/completions. Picking the wrong one fails validation on the request body, not on authentication, so the error won't mention your key.
Two constraints in this shape produce silence rather than errors. The model_provider value has to match the table name character for character, and a typo means the agent quietly uses its built-in default. You also can't name a custom provider openai, ollama or lmstudio — those identifiers are reserved.
OpenCode does the same job in JSON, with a provider block in opencode.json at the project root or in your global config directory. It names an npm adapter for the wire format, interpolates the key from the environment rather than storing it, and selects a default model as <provider-key>/<model-id>. A JSON syntax error here drops the provider block entirely, and changes don't take effect until you restart.
Settings-screen fields
IDE extensions usually ask for the same values in a form: pick an "OpenAI Compatible" provider, paste the base URL and key, and type the model ID. The catch is that a form can ask for things an environment variable never does — the model's context window, maximum output tokens, image support and tool-calling support, declared by hand, because the extension can't discover them from a generic endpoint. Those fields aren't decoration. An understated context window truncates long sessions, and an unchecked tool-calling box can disable the behaviour that makes the agent an agent.
Get the model ID right, because nothing else will tell you
Model IDs aren't portable, and the correct spelling depends on both the tool and the compatibility surface you're using. The same model behind the same gateway can have three legal spellings:
| How you reach it | The ID looks like | Why |
|---|---|---|
| An Anthropic-format agent on an Anthropic-compatible surface | anthropic/claude-sonnet-5 |
The surface namespaces IDs by provider |
| A Codex-style or OpenAI-format agent on an OpenAI-compatible surface | claude-sonnet-5 |
The surface uses flat catalogue IDs with no prefix |
| An agent that requires its own provider prefix | openai/claude-sonnet-5 or avis/claude-sonnet-5 |
The prefix identifies the provider block, not the model |
That third row is the one that catches people. When a tool asks for <provider>/<model>, the prefix is the name you gave the provider in your own config. It isn't part of the catalogue ID, and adding it on an endpoint that expects a flat ID returns a 404 for a model that exists.
Rather than trusting any example, list the catalogue. An OpenAI-compatible endpoint exposes GET /v1/models, so one curl with your key returns the exact IDs it will accept:
curl https://api.avis.xyz/api/openai/v1/models \
-H "x-api-key: $AVIS_API_KEY" | jq '.data[].id'
Copy an ID straight out of that response. It's also worth setting the cheap model separately if your agent supports it: several agents call a small background model constantly for summarising, file naming and similar chores, and pointing that at an inexpensive model keeps it off your main budget.
Verify the connection, then find out what broke
Test the endpoint with curl before you debug the agent. Agents wrap errors in their own messages, and a two-line curl separates "the endpoint is wrong" from "the agent is configured wrong" immediately.
The smallest test that proves the connection
Send one request to the endpoint your agent would call, with the header your credential variable would use. Then read the response for what it proves, not for whether it succeeded. A normal completion means everything works. An error naming an unknown model is also a pass — the endpoint accepted your credential and got far enough to reject the model name, so the URL and the key are both right and only the model ID is wrong. You don't need to guess a valid model to prove the connection.
Once the agent is running, check its own status output. Most will show the active base URL and credential source, which catches the case where you set a variable in one terminal and launched the agent from another. If the base URL still shows the original vendor, your configuration never reached the process.
When it returns 401 or 404
| What you see | Usually means |
|---|---|
401 on every request |
The credential is in a variable that sends the wrong header, or a leftover key from the original vendor is taking precedence |
404 on a model that exists |
The model ID has a prefix the surface doesn't want, or is missing one it does |
400 naming a request field |
The wire format is wrong for this endpoint, or the endpoint rejects a field the agent sends |
| The agent asks you to log in although curl works | The credential isn't somewhere the agent reads before startup — a reachable base URL isn't a credential |
| Connection refused, or the host doesn't resolve | The base URL is wrong, or a VPN or firewall blocks the path |
When plain chat works but the agent doesn't
A text-generation smoke test isn't a compatibility test. Agents need tool calling and streaming, and both break in ways a "write me a haiku" request never reveals.
The clearest example is tool calling over a stream. Tool calls arrive as a sequence of partial chunks the client reassembles, and each chunk carries an index saying which call it belongs to. If an endpoint assigns the tool call's ID under one index and streams its arguments under another, a client following the specification treats them as two different calls and fails on a missing ID. Non-streaming requests to that same endpoint succeed. That's the whole reason chat works and the agent loop doesn't.
Buffering is the other common one. An intermediate layer that collects the full response and releases it at once still returns valid output, so tests pass, but the stream stops being a stream. Compare time to first token against total time: if they're nearly identical, something is buffering. Worth checking too — whether token counts come back on streamed requests, and how the endpoint behaves when a request fails halfway through rather than before it starts. One agent's documented floor for a custom provider is tool-calling and streaming support plus a recommended 128k context window, which is a reasonable bar in general.
What stops working
Repointing an agent costs you things, and they're worth knowing before you commit:
- Features tied to the vendor's account. Anything authenticating against the vendor's own identity — remote session pairing, voice input, usage dashboards — stops working while a custom credential is active, because the agent is no longer using your login.
- Checks that ignore your base URL. Some availability checks call the vendor's host directly whatever the base URL says, so they fail on a restricted network even when inference is fine.
- New agent features the endpoint doesn't forward. An endpoint that passes unrecognised request fields through keeps working across agent releases; one that validates against a fixed schema starts rejecting whole requests as soon as the agent adds a field.
- Official support for the combination. At least one vendor states plainly that it doesn't support routing its agent to non-vendor models through any gateway. It generally works, but it isn't a supported configuration, and for a team that's a real consideration.
None of that argues against doing it — it argues for doing it deliberately. Pick an endpoint that forwards fields it doesn't recognise, keep the original configuration ready to switch back to, and don't build a workflow on a feature that depends on the vendor's own login.
FAQ
Can I use a coding agent with a model its vendor doesn't sell?
Usually yes, if the agent exposes a custom base URL and the endpoint speaks a format the agent understands. An Anthropic-format agent needs an Anthropic-compatible endpoint and an OpenAI-format agent needs an OpenAI-compatible one, so a gateway offering both surfaces covers either case. Be aware that at least one vendor documents this as unsupported even where it works.
Should the base URL end in /v1?
It depends on what the agent appends. An agent that adds /v1/messages itself wants a base URL with no /v1, while most OpenAI-compatible configurations want the root including /v1. Neither wants the full /chat/completions path. If in doubt, remove the last path segment the agent is going to add, then test with curl.
Why am I getting a 401 right after setting the base URL?
Two causes cover nearly every case. Either your key is in the variable that sends the wrong HTTP header, so the endpoint never reads it, or a credential left over from the agent's original vendor is taking precedence over the one you just set. Clear the old variable explicitly, and if you guessed between an auth-token and an api-key variable, try the other one.
Why does the agent say the model doesn't exist when I can see it in the catalogue?
The ID is almost certainly spelled for a different surface. Some surfaces namespace IDs as provider/model while others use flat IDs, and some agents additionally require a prefix naming your own provider block. List the endpoint's models and copy the ID from the response rather than from an example.
Do I need a gateway, or can I point the agent straight at a provider?
Either works. Pointing straight at one provider is simpler and enough if you only want that provider's models. A gateway earns its place when you want several providers behind one credential, one bill for a team, a provider key that stays server-side, or the ability to change model without touching every developer's machine.
Wrapping up
To point a coding agent at a different model provider, you need a base URL that stops before the path the agent appends, a credential in the variable that sends the header your endpoint reads, and a model ID spelled the way that endpoint spells it. Then prove it with curl, check the agent's own status output, and test tool calling and streaming rather than plain text before you trust it with real work.
If you'd rather configure one endpoint and reach several providers' models through it, AVIS documents this setup for Claude Code, Codex CLI, Copilot CLI, OpenCode and Gemini CLI, with an OpenAI-compatible surface for anything else that speaks that format. Keys are created under Settings → API Keys and shown once, and each model's own price is passed through unchanged with a 10% platform fee on API usage.
Last updated: September 2026