Docs

opencode

opencode builds its providers on the Vercel AI SDK, so pointing it at a new endpoint is one block naming an npm package and a baseURL. Which package you name decides which of the two wire formats it uses.

One provider block in opencode.json — @ai-sdk/openai-compatible for chat completions, @ai-sdk/openai when you want the /v1/responses surface.

opencode.json
{
  "$schema": "https://opencode.ai/config.json",
  "provider": {
    "kunavo": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "Kunavo",
      "options": {
        "baseURL": "https://api.kunavo.com/v1",
        "apiKey": "{env:KUNAVO_API_KEY}"
      },
      "models": {
        "claude-sonnet-5": {
          "name": "Claude Sonnet 5",
          "limit": { "context": 200000, "output": 64000 }
        },
        "claude-haiku-4-5": { "name": "Claude Haiku 4.5" }
      }
    }
  }
}
The npm field picks the wire format. @ai-sdk/openai-compatible speaks /v1/chat/completions; @ai-sdk/openai speaks /v1/responses. Kunavo serves both, so either works — use the Responses package when you want reasoning items carried through on the GPT family, and the chat-completions package for everything else.
"apiKey": "{env:KUNAVO_API_KEY}" reads the key from the environment at load time. opencode.json is a file that ends up in repositories; a literal key in it does not stay secret.
Set limit.context and limit.output per model. opencode tracks remaining context against those numbers, so a model left without them is budgeted against a default that is not its own.

Step by step

  1. Create a key at /app/keys and copy it — it is shown once.
  2. Export it: export KUNAVO_API_KEY=sk-kn-...
  3. Add the provider block to opencode.json — the global one at ~/.config/opencode/opencode.json for every project, or the one in the project root for just this repository.
  4. Start opencode and pick the model from the model list; the provider appears under the name you gave it.
  5. To add a model later, add another key under models — the id is what goes on the wire, the name is only a label.

Checked against opencode's providers documentation on September 6, 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 opencode.

# 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 idKunavo in / outWhere it fits in opencode
claude-sonnet-5$2.00 / $10.00the default build model
claude-opus-5$2.00 / $10.00plan mode, where the plan drives everything after it
claude-haiku-4-5$0.40 / $2.00sub-agent and search work, where request count is high
gpt-5-6-sol$2.00 / $12.00use it with @ai-sdk/openai so reasoning items survive the round trip
Billing is per token from a prepaid balance with no monthly fee — see billing. On repeated context — which is most of what an editor or a chat client sends — prompt caching moves the bill more than the model choice does.

FAQ

How do I add a custom provider to opencode?

Add a block under "provider" in opencode.json naming an npm package, a display name, options.baseURL, options.apiKey and a models map. Use @ai-sdk/openai-compatible for an endpoint that serves /v1/chat/completions and @ai-sdk/openai for one that serves /v1/responses. The provider then appears in opencode's model list under the name you gave it.

How do I keep the API key out of opencode.json?

Use the {env:VAR_NAME} interpolation syntax in options.apiKey — for example "apiKey": "{env:KUNAVO_API_KEY}" — and export the variable in your shell. opencode resolves it when it loads the config, so the file stays safe to commit alongside the project it configures.

What is the difference between @ai-sdk/openai and @ai-sdk/openai-compatible in opencode?

They select different endpoints on the same base URL. @ai-sdk/openai-compatible calls /chat/completions, which nearly every gateway implements; @ai-sdk/openai calls /responses, the newer OpenAI surface. Pick the one your endpoint actually serves — using the wrong package produces a 404 from a base URL that is otherwise correct.

Why does opencode run out of context earlier than expected?

Because the model entry has no limit block, so opencode budgets it against a default rather than the model's real window. Add "limit": { "context": <window>, "output": <max output> } to that model in opencode.json using the figures from the provider's catalog, and the context readout and compaction points line up with reality.