Home Assistant: a voice assistant on Claude via API

Home Assistant's built-in voice assistant can run on any catalogue model through one API key. Configuration and the failure modes to expect.

4 min · · Updated · автоматизация · home assistant

Home Assistant's conversation engine can connect to an external OpenAI-compatible API — which is exactly the protocol the gateway serves at https://api.zukko.pro/v1. From there the voice assistant works as usual: Assist hears the command, turns it into text, the text goes to a model, the reply gets spoken back — the only difference is whose key and whose balance sit behind the answer.

Basic configuration

For integration with a generic OpenAI-compatible provider (the community's widely used Extended OpenAI Conversation, for instance), setup through the UI or YAML comes down to three fields:

bash
base_url: "https://api.zukko.pro/v1"
api_key: "fetch_your_key"
model: "gpt-5.6-sol"

The integration builds the auth header itself — Authorization: Bearer fetch_... — there's nothing to add manually. Any catalogue model works (the current list is at GET https://api.zukko.pro/v1/models): claude-sonnet-5, say, or the light glm-5.3-flash if you want to minimize the gap between the command and the spoken response — which one works better for your commands is worth checking in practice.

Test the key before debugging the YAML

Before digging into why Home Assistant is erroring, confirm the key works on its own:

bash
curl https://api.zukko.pro/v1/chat/completions \
  -H "Authorization: Bearer fetch_your_key" \
  -H "Content-Type: application/json" \
  -d '{"model":"gpt-5.6-sol","messages":[{"role":"user","content":"turn on the kitchen light"}]}'

If curl works and the HA integration doesn't, the issue is the configuration, not access. The usual culprit is the same one that shows up everywhere: a missing or doubled /v1 — OpenAI-compatible integrations expect that suffix in the base address, and without it every request comes back 404.

A limit on the key, because this is a shared household device

A key wired into a voice assistant will likely get used by everyone in the house at once, and none of them will see the bill for any single request. It's worth setting a monthly dollar spending limit on that key's page in the dashboard right away — if a kid discovers the speaker will answer anything, the limit stops the spend long before it shows up on the monthly statement. The same page lets you restrict which models that key is allowed to call, so the voice assistant can't accidentally reach for a pricier model than household commands need.

Watching the balance from inside Home Assistant

Since HA can hit any REST API, it's easy to set up a sensor that tracks the balance itself:

bash
sensor:
  - platform: rest
    name: zukko_balance
    resource: https://api.zukko.pro/v1/balance
    headers:
      Authorization: !secret zukko_api_key
    value_template: "{{ value_json.balance_cents / 100 }}"
    unit_of_measurement: "$"
    scan_interval: 3600

That sensor can drive an automation — a phone notification when the balance drops below some threshold, before the voice assistant suddenly goes quiet with a 402.

What happens on failure

When the balance runs out, the response is {"error":{"message":"Insufficient balance","code":"insufficient_balance"}}, and Home Assistant will either speak a generic error phrase or say nothing, depending on how the integration is configured. It helps to remember that balance is checked before the request reaches the model, so that failed attempt costs nothing, and a retry right after topping up works immediately, no HA restart needed.

If the model named in your config gets switched off, the response comes back as a 503 with model_disabled; the current model list is at GET https://api.zukko.pro/v1/models or on the Models page in the dashboard. Worth checking HA's logs — if the voice assistant suddenly stops responding with no visible config change, this is a likely cause.

Simultaneous requests from multiple household members

The gateway doesn't throttle simultaneous requests as such — it has no rate limit of its own. But if the whole household uses the assistant heavily and the key has a tight daily or monthly dollar spending limit, you may see a 429 with key_daily_limit_reached (resets at 00:00 UTC) or key_monthly_limit_reached, depending on which limit ran out. You can tell this apart from overload at the model provider (a 429 without these codes comes from the provider) by the response body: a code pointing at the key means a limit setting.

Idempotency on flaky Wi-Fi

Voice speakers often sit on a weak Wi-Fi signal, and a request occasionally cuts off mid-flight with no reply. If the integration supports custom headers, it's worth generating an Idempotency-Key for every voice request and reusing it on automatic retry: if the original request actually went through server-side, resending with the same idempotency key within 24 hours returns the same response without charging twice.

If a model is temporarily unavailable

A 503 with provider_unavailable or model_disabled means a specific model is switched off in the catalogue for a while — not a Home Assistant outage, not a key problem. If the integration config points at only one model, it's worth keeping a second connection profile with a different model on standby (say, glm-5.3 instead of gpt-5.6-sol) and switching to it via automation if the main one returns 503 a few times in a row. This matters more for a household voice assistant than for a chat interface, where you can just reread the error and retry: a speaker in this situation either repeats a generic error phrase or stays silent, and there's no way to tell which without checking the HA logs.

What to log for later troubleshooting

If several people in the house use Assist, it helps to log at least the error code and timestamp of every failed request, via logger in the conversation component's config. That way, working out "why didn't the assistant answer last night" doesn't mean guessing between four different causes — empty balance, key limit, a disabled model, or a plain router hiccup. Each one needs a different fix, not the same "restart Home Assistant" that usually doesn't solve anything.

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_*

Home Assistant + Claude API: your own voice assistant · Zukko