Docs

NextChat

A self-hosted NextChat reaches Kunavo through the variables it already has for OpenAI: OPENAI_API_KEY holds the key, BASE_URL holds the origin, CUSTOM_MODELS holds the ids. No fork, no patch.

Three environment variables — OPENAI_API_KEY, BASE_URL (bare origin) and CUSTOM_MODELS — put a self-hosted NextChat on Claude, GPT and Gemini through one key.

Environment variables — Vercel project settings, docker -e, or .env.local
CODE=your-access-password
OPENAI_API_KEY=sk-kn-...
BASE_URL=https://api.kunavo.com
HIDE_USER_API_KEY=1
CUSTOM_MODELS=-all,+claude-sonnet-5@OpenAI,+claude-opus-5@OpenAI,+claude-haiku-4-5@OpenAI,+gpt-5-6-sol@OpenAI,+gemini-3-1-pro@OpenAI
BASE_URL takes the bare origin, with no /v1. The docs page has no sentence about the suffix, but its own row settles it: the documented default is https://api.openai.com, not https://api.openai.com/v1 — NextChat appends the rest of the path. A /v1 you add by hand becomes /v1/v1/chat/completions, which comes back as a 404 and reads like a broken endpoint rather than a typo.
Who holds the key is a deployment decision, not a default. With OPENAI_API_KEY set, the server calls Kunavo with your key and everyone who gets past CODE spends your balance. HIDE_USER_API_KEY=1 is documented as "If you do not want users to input their own API key, set this value to 1" — leave it unset instead and each visitor can paste their own key in Settings, which is the shape you want for a shared instance.
The -all prefix and the @OpenAI suffix are not in the documented table, which covers only +, - and name=displayName. They come from NextChat's own model-collection code, read on the same day: -all clears the built-in list so the picker does not offer ids Kunavo will reject, and @OpenAI pins each new id to the OpenAI provider so it is sent to BASE_URL. An id written without that suffix gets a provider named after itself and does not travel the path you configured. The capitalisation is literal.
Kunavo serves no text-to-speech or speech-to-text model, so NextChat's voice features have nothing to answer them here. Leave ENABLE_BALANCE_QUERY unset as well: the balance check calls OpenAI's own dashboard billing routes, which are not part of the OpenAI-compatible surface. Your balance lives on /app/billing.
This page was read off NextChat's documentation, not off a test run — Kunavo has not executed NextChat against this endpoint, and a published setup page is not a compatibility test. Chat completions are the documented surface on both sides; anything past that is untested here.

Step by step

  1. Create a key at /app/keys and copy it — it is shown once.
  2. Set OPENAI_API_KEY to that key and BASE_URL to https://api.kunavo.com. In Vercel these are project environment variables; in Docker they are -e flags; locally they go in .env.local.
  3. Set CODE to an access password, or the deployment is open to whoever finds the URL while your key is the one paying.
  4. List the ids you want in CUSTOM_MODELS, each with the @OpenAI suffix, starting with -all to drop the built-ins.
  5. Redeploy — environment variables are read by the server, so a Vercel project needs a new deployment and a container needs a restart. Editing the variable alone changes nothing in a running instance.
  6. Open the app, pick one of your ids in the model selector and send a message. If the reply arrives, the three variables agree.

Checked against NextChat's Environment Variables 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 NextChat.

# 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 NextChat
claude-sonnet-5$2.00 / $10.00the everyday thread — long conversations without watching the balance
claude-opus-5$2.00 / $10.00the one question a week that deserves the expensive model
claude-haiku-4-5$0.40 / $2.00summarising, renaming chats, and the short back-and-forth that dominates a shared instance
gpt-5-6-sol$2.00 / $12.00a second family in the same picker, on the same key
gemini-3-1-pro$0.70 / $4.20long pasted documents, where the context window is the deciding feature
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 point NextChat at a custom API endpoint?

Set BASE_URL to the endpoint's origin and OPENAI_API_KEY to the key it issued, both as environment variables on the self-hosted deployment. NextChat's own Environment Variables page documents BASE_URL as "Override OpenAI API request base URL", so nothing needs patching: the app keeps speaking the OpenAI wire format and sends it somewhere else. Redeploy afterwards — the values are read on the server, so a running instance does not pick them up.

Does the NextChat BASE_URL need /v1 at the end?

No. NextChat appends the version segment and the route itself, which is why the documented default value for BASE_URL is the bare origin https://api.openai.com rather than https://api.openai.com/v1. Write https://api.kunavo.com and nothing more. Adding the suffix yourself produces a doubled path and a 404, which is easy to misread as the endpoint being down.

How do I add a custom model to NextChat's model list?

CUSTOM_MODELS takes a comma-separated list where + adds a model, - hides one and name=displayName renames it. An id NextChat does not know is created on the spot, so +claude-sonnet-5@OpenAI puts that id in the picker. Two details are worth copying exactly: start the list with -all so the built-in OpenAI ids disappear instead of failing when someone picks one, and keep the @OpenAI suffix so the id is routed through BASE_URL rather than to a provider named after the model.

Should NextChat use a server API key or let each user paste their own?

Both are supported and the choice is about who pays. A key in OPENAI_API_KEY is the server's, so every visitor past the CODE password spends from that one balance — sensible for a private instance, expensive for a shared link. Leaving HIDE_USER_API_KEY unset lets a visitor enter their own key in Settings and pay for themselves; setting it to 1 removes that field. Either way the key never reaches the browser unless the user typed it there.