Docs
OpenHands
OpenHands routes every model call through LiteLLM, so the endpoint is two fields that have to agree: a model id carrying an openai/ prefix, and a base URL that keeps its /v1. Get that pair right and the Advanced tab is talking to Claude, GPT and Gemini on one key.
Settings → LLM → Advanced takes three fields — Custom Model, Base URL, API Key — with the model id carrying an openai/ prefix and the base URL keeping its /v1.
# Settings → LLM → Advanced (toggle "Advanced" on first)
Custom Model openai/claude-sonnet-5
Base URL https://api.kunavo.com/v1
API Key sk-kn-...
# The "openai/" prefix is the provider, not a vendor: it tells OpenHands to
# speak the OpenAI Chat Completions protocol to the Base URL above. The model
# id after the slash is Kunavo's, and resolves at Kunavo.
#
# Keep the /v1. It belongs to the openai/ prefix — a litellm_proxy/ model
# takes the bare origin instead, which is the opposite convention./v1, and keep the openai/ prefix — they are one decision, not two. OpenHands' settings page says only “If your provider has a specific base URL, specify it here”, so the field itself does not settle the form. The prefix does. Its Configure a Model page prescribes openai/<served-model-id> for an OpenAI-compatible server, taking the id “usually from its GET /v1/models endpoint”, and the one worked value it ever shows for that route's Base URL ends in /v1 — http://host.docker.internal:1234/v1 in the LM Studio walkthrough. The contrast is the proof: a litellm_proxy/ model is documented with a base URL of https://your-litellm-proxy.com, no /v1 at all. Mixing the two — openai/ against a bare origin, or a /v1 under litellm_proxy/ — is the common way this ends in a 404 rather than a 401.openai/ examples are local servers — LM Studio, Ollama, vLLM, SGLang. Its documentation shows no worked example of a remote OpenAI-compatible gateway, so what is quoted above is the prefix rule and the shape of the value, not a page written about this case. If OpenHands later documents one, that page is the authority.curl below is the part you can settle in ten seconds; the client's behaviour is between you and OpenHands.LLM_EMBEDDING_MODEL and LLM_EMBEDDING_DEPLOYMENT_NAME unset, and keep whatever provider already backs any vector index or audio step in your setup.Step by step
- Create a key at
/app/keysand copy it — it is shown once. - Open Settings → LLM and turn on the Advanced toggle. The three fields appear in the order Custom Model, Base URL, API Key.
- Type the model id with its prefix —
openai/claude-sonnet-5, notclaude-sonnet-5. The ids Kunavo serves are the onesGET /v1/modelsreturns, which is the same list OpenHands' own documentation tells you to take a custom id from. - Paste
https://api.kunavo.com/v1into Base URL and your key into API Key, then Save Changes. OpenHands documents that saving a local profile validates the configuration against the backend first and blocks the save when it fails, so an error here is a real rejection, not a cosmetic one. - Check what the backend can reach, not what your browser can. The base URL has to resolve from the machine running the Agent Server — the documentation is explicit that if Agent Canvas runs in Docker,
127.0.0.1is the container. A public endpoint like Kunavo's is the easy case; a corporate proxy in front of it is not. - Start a new conversation and give it a task that reads and edits a file. OpenHands notes that a saved LLM applies to new conversations and that older ones must be restarted first, and a run that touches the tools tells you more about the pairing than a greeting does.
Checked against OpenHands' Language Model (LLM) Settings page on September 21, 2026. Third-party settings move; if a field name here no longer matches what you see, that page is the authority, not this one.
Verify before you debug the client
One request settles whether a failure is the endpoint, the key, or the configuration file. If this returns JSON, the same base URL and key work in OpenHands.
# Settles whether a failure is the endpoint, the key, or the client.
curl -sS https://api.kunavo.com/v1/models \
-H "Authorization: Bearer sk-kn-..."Which model id to put in the field
Every text model is reachable as a model id — the live list is GET /v1/models, and the catalog with prices is on the models page. Rates are USD per 1M tokens, input / output.
| Model id | Kunavo in / out | Where it fits in OpenHands |
|---|---|---|
claude-sonnet-5 | $2.00 / $10.00 | the everyday working model — enter it as openai/claude-sonnet-5 |
claude-opus-4-8 | $2.50 / $12.50 | the model OpenHands' own index table puts at the top of the Claude family |
claude-haiku-4-5 | $0.40 / $2.00 | a cheap profile for routine edits, switched back mid-conversation |
gpt-5-6-sol | $2.00 / $12.00 | a second family on the same key and the same Base URL |
gemini-3-1-pro | $0.70 / $4.20 | a third opinion when a plan keeps going sideways |
Three boundaries worth knowing before you debug
OpenHands has more moving parts than a single-process CLI, and two of them look like the LLM endpoint without being it. These come from its own documentation, read on the date above:
- The sandbox is not the model. OpenHands runs your work in an agent-server sandbox and calls the model over the network; those are separate surfaces with separate credentials. A key configured here buys model calls. It has nothing to say about what the sandbox can reach, and a sandbox networking problem does not look like an authentication error.
- ACP agents are carved out entirely. Agent Canvas can delegate to Claude Code, Codex or Gemini CLI as an ACP agent, and the Configure a Model page states that those “manage their own model access” — so an LLM profile does not redirect that subprocess. If you expected traffic on your key and see none, check which agent is actually running. The OpenHands vs Claude Code comparison goes through that split, including the credential-priority rule that decides it.
- Profiles, and the 10-profile ceiling. A saved configuration becomes an LLM profile, the most recently saved one becomes active for new conversations, and profiles can be switched mid-conversation without losing context — which is the mechanism that makes a cheap id and an expensive id useful on one key. The documentation caps this at 10 profiles per account. A Provider Connection stores the provider, API key and optional base URL once for several profiles, and the same page notes that panel is available on local agent-server backends and hidden on an OpenHands Cloud backend.
FAQ
How do I point OpenHands at a custom API endpoint?
Open Settings → LLM and turn on the Advanced toggle, which OpenHands documents as the way to "set custom models as well as some additional LLM settings". Three fields appear, in this order: Custom Model, Base URL, API Key. Enter the model id with a provider prefix — openai/<model-id> for an OpenAI-compatible endpoint — put the endpoint in Base URL, paste your key, and click Save Changes. The saved configuration becomes an LLM profile, and it applies to new conversations; older ones have to be restarted to pick it up.
Does the OpenHands Base URL need /v1 at the end?
For an openai/-prefixed model, yes. The settings page itself only says to specify the base URL if your provider has a specific one, so it does not settle the form on its own — the prefix does. OpenHands' Configure a Model page prescribes openai/<served-model-id> for an OpenAI-compatible server and takes the id from its GET /v1/models endpoint, and the one worked Base URL it shows for that route, in the LM Studio walkthrough, is http://host.docker.internal:1234/v1. A litellm_proxy/ model is the opposite: its documented base URL is the bare proxy origin with no /v1. So for Kunavo the value is https://api.kunavo.com/v1.
Why does OpenHands refuse to save my LLM profile?
OpenHands validates a local profile against the backend before persisting it, and its documentation says that when validation fails — an invalid API key or an unavailable model are the examples it gives — the save is blocked and the error is shown. So a blocked save is a real rejection. Settle which half is wrong outside the client first: one curl against the endpoint's /v1/models with the same key returns JSON if the pair is good, a 401 if the key is wrong and a 404 if the URL is. Older backends that do not support validation skip the check and save normally.
Can OpenHands use Claude models through an OpenAI-compatible endpoint?
Yes. The openai/ prefix names a wire protocol, not a vendor: OpenHands sends an OpenAI-shaped chat completion to the base URL you configured and passes the id after the slash straight through, so a Claude id resolves at that endpoint rather than inside OpenHands. Worth remembering that OpenHands leans hard on tool calling and its own documentation says it needs a powerful model to work properly, so this is not the place for the cheapest id you can find.
Has Kunavo tested this setup in OpenHands?
No. What was checked, on September 21 2026, is OpenHands' own documentation — the field names, their order, the prefix rule and the base-URL shape are quoted from it. Kunavo has not run an OpenHands conversation against its endpoint and makes no claim here about authentication, streaming, tool round-trips or model routing inside a pinned client version. The one thing you can settle in isolation is whether the endpoint and the key work at all, which the curl on this page does.