Connect external providers
hal0 can route requests to external upstreams — third-party
OpenAI-compatible APIs like OpenAI, Anthropic, OpenRouter, Google AI
Studio, DeepSeek, MiniMax, Ollama, or any custom endpoint. Manage them
with hal0 upstream, a thin client over /api/upstreams /
/api/providers backed by a built-in catalogue of provider templates.
See what’s available
Section titled “See what’s available”hal0 upstream list # your configured upstreamscurl http://localhost:8080/api/providers/catalogThe catalogue is a static reference of known provider templates —
openai, anthropic, openrouter, google_ai_studio, deepseek,
minimax, ollama (local), and a generic custom OpenAI-compatible
entry — each carrying its default base URL, auth style, and models path.
Create an upstream
Section titled “Create an upstream”hal0 upstream create openrouter \ --catalog openrouter \ --auth-env OPENROUTER_API_KEY \ --api-key sk-or-...-
name(positional) — a unique id you’ll reference everywhere else. -
--catalog— the catalogue id to seed defaults from (base URL, auth style). Omit for a fully custom entry and pass--url/--auth-styleyourself. -
--url— override the base URL. -
--auth-style—bearer(Authorization: Bearer <key>— the default for most OpenAI-compatible endpoints, including OpenRouter and Ollama),header(custom header), ornone(unauthenticated, e.g. a local Ollama). -
--auth-header— the header name, when--auth-style header. -
--auth-env— the name of the environment variable that will hold the secret. The key itself never lands in TOML — only the env-var name does. -
--api-key/-k— write the credential in the same call. This posts to/api/providers/{name}/credentialsand binds it to the upstream’s declaredauth_value_env, so you can’t accidentally write a key the upstream won’t read. -
--timeout— request timeout in seconds (default300). -
--advertise-models/--hide-models— whether this upstream’s models appear in the aggregated model list. -
--enabled/--disabled.
Update, test, and remove
Section titled “Update, test, and remove”hal0 upstream update openrouter --timeout 120hal0 upstream test openrouterhal0 upstream show openrouterhal0 upstream advertise openrouter # toggle whether its models are advertisedhal0 upstream delete openroutertest probes the upstream’s /models endpoint and returns a
reachability report: {ok, status, latency_ms, models_count, error?}. If
the declared env var is empty, the probe fails fast without making an
HTTP call.
Write a credential without recreating the upstream
Section titled “Write a credential without recreating the upstream”hal0 upstream update openrouter --api-key sk-or-new-keyOr directly over the API:
curl -X POST http://localhost:8080/api/providers/openrouter/credentials \ -H 'content-type: application/json' \ -d '{"key":"OPENROUTER_API_KEY","value":"sk-or-..."}'The key must match the upstream’s declared env-var name exactly. The
endpoint writes the value atomically to the API’s env file with 0600
permissions, updates the running process environment immediately, and
never echoes the secret back — the response carries
"value": "***REDACTED***".
The dashboard’s Settings → Secrets panel writes the same file with presets for the common providers plus a paired AWS Bedrock entry, but it doesn’t check the name against any upstream’s declared env var — type the exact name your upstream expects.
Picking up changes
Section titled “Picking up changes”Credential writes apply to the running process immediately — no restart
needed. Structural edits to upstreams.toml (adding, removing, or
retiming an upstream through the CLI) are read once at hal0-api
startup — hal0 config reload re-reads hal0.toml, not
upstreams.toml. Run hal0 config validate after a hand-edit, then
systemctl restart hal0-api to pick it up.