Providers & API keys
A self-hosted SmartChats server calls model and service providers with your own API keys. This page lists every provider it can use, the environment variables it reads, and what each feature needs.
Providers
| Provider | Env vars (first match wins) | Used for | Needed by default? |
|---|---|---|---|
| Together AI | SMARTCHATS_TOGETHER_API_KEY, TOGETHER_API_KEY, TOGETHERAI_API_KEY | Open-weight chat models, including the default model. The default voice turn verdict. | Yes: default chat model and turn verdict |
| Azure Speech | SMARTCHATS_AZURE_SPEECH_KEY, AZURE_SPEECH_KEY plus SMARTCHATS_AZURE_SPEECH_REGION, AZURE_SPEECH_REGION | Text to speech (the default voice) | Yes: default voice |
| OpenAI | SMARTCHATS_OPENAI_API_KEY, OPENAI_API_KEY | GPT chat models, embeddings (semantic search), OpenAI text to speech | For semantic search: the only embeddings SmartChats uses today |
| Anthropic | SMARTCHATS_ANTHROPIC_API_KEY, ANTHROPIC_API_KEY | Claude chat models | No |
| Google (Gemini) | SMARTCHATS_GOOGLE_API_KEY, GOOGLE_API_KEY, GEMINI_API_KEY | Gemini chat and image models | No |
| xAI | SMARTCHATS_XAI_API_KEY, XAI_API_KEY | Grok chat models | No |
| Serper | SMARTCHATS_SERPER_API_KEY, SERPER_API_KEY | The agent’s web search tool | No |
| Your own S3-compatible bucket | SMARTCHATS_S3_ENDPOINT, SMARTCHATS_S3_ACCESS_KEY_ID, SMARTCHATS_S3_SECRET_ACCESS_KEY, SMARTCHATS_S3_BUCKET, optional SMARTCHATS_S3_REGION (default us-east-1) | Media storage, instead of the SeaweedFS that smartchats start runs | No |
The variable names come from packages/smartchats-local-server/src/config.ts. For each provider the server checks, in order:
SMARTCHATS_<PROVIDER>_API_KEY- the provider’s usual name, such as
TOGETHER_API_KEY(and any alias listed above) - a key stored in the database with
POST /keys
The SMARTCHATS_ name always wins. Use it when another tool on the machine already sets the plain name to a different key.
Chat models by provider
Each model in the registry (packages/cortex/src/model_registry.ts) belongs to one provider, and needs that provider’s key:
| Provider | Models |
|---|---|
| Together AI | DeepSeek V4.1 Flash (the default), GLM, Kimi, Qwen, MiniMax, gpt-oss |
| OpenAI | GPT-4.1, GPT-5 family, GPT-6 family, o4-mini |
| Anthropic | Claude Haiku, Sonnet, Opus and Fable |
| Gemini Flash, Flash Lite and Pro, plus an image model | |
| xAI | Grok |
The app’s preset menu greys out choices whose provider has no key. It reads availability from GET /local-api/keys/providers.
Setting keys
With the CLI
The CLI writes keys to a .env file that smartchats start loads on boot:
- Binary install (the
curl … | bashinstaller):~/.smartchats/.env. Docker mounts the same file. - Source install (a clone of the repo):
.envat the repo root.
smartchats env # walk the providers and set or update each key
smartchats env --list # show what is configured, masked
smartchats setup # first run: also prompts for keys
smartchats restart # pick up changes on a running stacksmartchats env and setup prompt for OpenAI, Anthropic, Google and Serper. For any other provider, add the line to the .env file yourself. smartchats start passes every variable in that file to the server, and a variable already set in your shell wins over the file.
# ~/.smartchats/.env
TOGETHER_API_KEY=...
AZURE_SPEECH_KEY=...
AZURE_SPEECH_REGION=eastus
XAI_API_KEY=...With POST /keys
Keys can also be stored in the database, through the BYO Keys section of the app’s settings or directly:
curl -X POST http://127.0.0.1:3000/local-api/keys \
-H 'Content-Type: application/json' \
-d '{"keys": {"together": "..."}}'| Route | What it does |
|---|---|
POST /local-api/keys | Stores keys for openai, anthropic, google, xai, together or azure |
GET /local-api/keys | Masked preview of stored keys only (not env vars) |
DELETE /local-api/keys/:provider | Removes a stored key |
GET /local-api/keys/providers | Which LLM and TTS providers have a usable key, and from where |
These work as shown from the machine running SmartChats. From another machine, add -H "Authorization: Bearer $SMARTCHATS_API_KEY" (set that variable on the server first).
Env vars shadow stored keys. A key from the environment is used before a stored one. POST /keys saves the key anyway and returns a warning naming the providers it will ignore at call time.
Some keys are env-only. Text to speech reads the Azure and OpenAI keys from the environment only, so an Azure key stored with POST /keys does not enable the voice. The Serper key and the S3 settings also come only from the environment.
What each feature needs
| Feature | Needs | Without it |
|---|---|---|
| Chat with the default model | Together | The request fails with no together API key configured, naming SMARTCHATS_TOGETHER_API_KEY and POST /keys. Pick a model from a provider you have a key for. |
| Chat with any other model | That model’s provider | Same error, naming that provider |
| Voice turn verdict (default judge) | The verdict model’s provider. Together for the default deepseek-v4.1-flash | The server skips the verdict and answers the turn, as if it had said COMPLETE. Turns are never held, so a pause mid-sentence ends the turn. |
| Default voice (Azure Emma) | Azure key and region | With an OpenAI key, the reply is spoken with an OpenAI voice instead. With neither, there is no speech. |
| OpenAI voices | OpenAI | Falls back to Azure if Azure is configured |
Embeddings (text-embedding-3-small) | OpenAI | The embeddings route returns an error. Saved logs are written without a vector, and search by meaning does not work. Adding a key later does not embed entries saved before it. |
| Web search | Serper | The search tool returns search disabled: SMARTCHATS_SERPER_API_KEY or SERPER_API_KEY not set |
| Media storage | No key: smartchats start runs SeaweedFS (bundled with binary installs) | Without SeaweedFS and without your own SMARTCHATS_S3_* bucket, storage routes return 501 and uploads are off. |
The turn verdict needs token probabilities. Judge v2 decides from the model’s probability that the user is done, read from the first token’s logprobs. In llm-service only the Together adapter returns them today. With a verdict model from another provider, the verdict is decided from the sampled word and its threshold has no effect. A verdict is one short call per voice turn, well under a cent with the default model. See Voice engine.
See also
- Voice engine: how the turn verdict uses these keys
- Self-Hosting: env-var management, bearer-token auth and Docker
- CLI Reference:
smartchats envandsmartchats setup - TURN_VERDICT.md: the verdict’s providers and self-hosting note