LibreChat: Zukko as a custom endpoint in librechat.yaml

LibreChat connects third-party providers through librechat.yaml. Here is a ready config for Zukko, the Docker mount and the usual errors.

5 min · · development · автоматизация

LibreChat is a self-hosted chat that supports many providers. Third-party OpenAI-compatible APIs are added as a custom endpoint in librechat.yaml. Below is a working config for Zukko, the key and Docker setup, a cost estimate, and the errors that most often keep the endpoint from showing up in the UI.

How the setup works

According to the LibreChat docs, a custom endpoint involves three files:

  • librechat.yaml — the endpoint definition: name, API address, models, display options;
  • .env — secrets that librechat.yaml references via ${VARIABLE_NAME};
  • docker-compose.override.yml — mounts librechat.yaml into the container (Docker only).

librechat.yaml and .env live in the LibreChat project root. You can point to a different config path with the CONFIG_PATH variable.

LibreChat custom endpoint for Zukko: librechat.yaml

bash
version: 1.3.17 # check the current config version in the LibreChat docs
cache: true
endpoints:
  custom:
    - name: "Zukko"
      apiKey: "${ZUKKO_API_KEY}"
      baseURL: "https://api.zukko.pro/v1"
      models:
        default: ["claude-sonnet-5", "claude-opus-5-5", "gpt-5.6-sol", "gpt-5.6-luna", "glm-5.3-flash"]
        fetch: true
      titleConvo: true
      titleModel: "glm-5.3-flash"
      modelDisplayLabel: "Zukko"

Field by field:

  • custom is a list, so every endpoint starts with a dash. A common mistake is writing it as a plain mapping without - name:.
  • name — the label shown in the endpoint selector.
  • apiKey — a reference to an environment variable. The LibreChat docs recommend this over putting the key in the file.
  • baseURL — the Zukko address, with /v1.
  • models.default — an array of models that is always shown. It's required even when fetch is on.
  • models.fetch: true — LibreChat asks the API for its model list. Zukko serves it at GET /v1/models, so new models appear without editing the config.
  • titleConvo and titleModel — LibreChat generates conversation titles itself. For this background task the cheapest model makes sense.
  • modelDisplayLabel — the model label in the chat.

The key in .env

bash
ZUKKO_API_KEY=fetch_your_key

Don't put your Zukko key in OPENAI_API_KEY: that variable belongs to the built-in OpenAI endpoint, and LibreChat would send it to OpenAI's servers. The LibreChat docs warn about a similar name clash for OpenRouter, so use your own variable names for custom endpoints.

Docker setup

For the container to see librechat.yaml, copy the example override file:

bash
cp docker-compose.override.yml.example docker-compose.override.yml

and uncomment the config mount:

bash
services:
  api:
    volumes:
      - type: bind
        source: ./librechat.yaml
        target: /app/librechat.yaml

Docker Compose merges the override file with the main docker-compose.yml automatically, so your changes survive LibreChat updates.

After any change to librechat.yaml or .env, restart LibreChat:

bash
docker compose down && docker compose up -d

If you run it locally without Docker, stop the process (Ctrl+C) and run npm run backend again.

Verification

  1. Open LibreChat and find Zukko in the endpoint list.
  2. Pick a model, for example claude-sonnet-5, and send a short message.
  3. If the endpoint is missing, check the logs: docker compose logs api. You can also validate the config with the YAML validator linked from the LibreChat docs.

Two keys: split the spending

If LibreChat is used both for work and for experiments, create two keys with different limits in the Zukko dashboard and define two endpoints:

bash
endpoints:
  custom:
    - name: "Zukko Work"
      apiKey: "${ZUKKO_WORK_KEY}"
      baseURL: "https://api.zukko.pro/v1"
      models:
        default: ["claude-sonnet-5", "claude-opus-5-5"]
        fetch: false
    - name: "Zukko Lab"
      apiKey: "${ZUKKO_LAB_KEY}"
      baseURL: "https://api.zukko.pro/v1"
      models:
        default: ["glm-5.3-flash", "gpt-5.6-luna"]
        fetch: false

With fetch: false users only see the listed models. To be safe, restrict the key itself too: every key in the dashboard has an allowed-models list and daily and monthly limits (see the key limits documentation).

Model choice and cost

ModelInput / output, $ per 1M tokensContext
claude-opus-5-50.3 / 0.31M
claude-sonnet-50.3 / 0.31M
gpt-5.6-sol0.3 / 0.31M
gpt-5.6-luna0.1 / 0.11M
glm-5.30.1 / 0.1128K
glm-5.3-flash0.04 / 0.04128K

What a conversation title costs: say the title request takes 500 tokens including the answer. On claude-sonnet-5 that's 500 × $0.3 / 1,000,000 = $0.00015; on glm-5.3-flash, 500 × $0.04 / 1,000,000 = $0.00002. Small, but it adds up with every new conversation, so set titleModel explicitly.

For the main conversations, choose a model based on your own tasks: take 10–20 typical requests, run them on two or three models, and compare the answers and spending in the dashboard's "Usage" section. For the current model list, call GET https://api.zukko.pro/v1/models or open the "Models" page in the dashboard.

Common problems

Zukko doesn't appear in the endpoint list

Check in order: the file is in the project root (or CONFIG_PATH is set); in Docker it's mounted via the override file; the YAML has no indentation errors; the endpoint has all required fields — name, apiKey, baseURL, models.default. LibreChat may silently skip an incomplete block, so check docker compose logs api.

401: invalid key

The variable name in .env doesn't match the one in apiKey, or LibreChat wasn't restarted after editing .env. The key must start with fetch_ and be copied in full.

404

baseURL is missing /v1, or has an extra /chat/completions. It must be exactly https://api.zukko.pro/v1.

400 on Claude models

claude-sonnet-5 and claude-opus-5-5 may reject a request with a 400 error if temperature is anything other than 1 or top_p is below 0.99. By default LibreChat doesn't send these parameters for custom endpoints, but users can set them in the conversation settings. For an endpoint with only Claude models you can add dropParams: ["temperature", "top_p"], and LibreChat will strip them from requests.

402 and 403

402 means the balance ran out; top it up in the dashboard. 403 model_not_allowed_for_key means the model isn't on this key's allowed list.

FAQ

Can each user enter their own key?

Yes. The LibreChat docs allow apiKey: "user_provided", in which case users enter the key in the UI themselves. That way everyone pays from their own Zukko balance.

Can I connect Zukko using the Anthropic format?

LibreChat supports provider: "anthropic" for custom endpoints, and Zukko accepts the Anthropic format too. But with the native Anthropic client the address goes without /v1, and the model list has to be written out by hand. The OpenAI-compatible option is simpler: the gateway converts requests to Claude for you.

Where can I see spending?

In the Zukko dashboard under "Usage". If you use separate keys for different endpoints, spending is split by key.

Where do I start?

Create a key in the Zukko dashboard, put it in .env, and copy the config above. For more on the API formats, see the Chat Completions documentation.

zukko

Pay 25×
less

for Claude Code & ChatGPT

One fetch_* key and base URL api.zukko.pro — for Cursor, Claude Code, Codex, and any SDK.

Get API keyDocumentation

api.zukko.pro · fetch_*

LibreChat custom endpoint: add Zukko in librechat.yaml · Zukko