How to Set Up Your DeepSeek API Key and Models in DeepSeek Harness
Configure a DeepSeek API key in DeepSeek Harness via Settings → Models or settings.yaml, understand .credentials.yaml, and fix MISSING_CREDENTIAL errors.
To use DeepSeek Harness (dsh), open the Web UI, go to Settings → Models, and paste your DeepSeek API key into the DeepSeek card's key field — no restart required. Under the hood, the key is written to $DSH_HOME/.credentials.yaml, kept separate from the non-sensitive settings.yaml, and resolved through a defined precedence chain if you'd rather set it via environment variable. This guide covers where credentials live, how resolution order works, and the two most common auth errors.
Where model configuration lives
dsh splits model configuration into two files, both stored under $DSH_HOME (default ~/.dsh) and shared across all profiles — unlike a profile's cordis.patch.yml, which is scoped to a single profile:
| File | Contents |
|---|---|
$DSH_HOME/settings.yaml | Non-sensitive config: model routing, custom provider base URLs, protocol, model lists |
$DSH_HOME/.credentials.yaml | API keys and other secrets |
The Web UI's Settings page writes to both automatically. If you save a key through Settings → Models, you get back a masked reference descriptor on screen — the plaintext key is never echoed back to the UI after saving.
Setting up the DeepSeek provider
The simplest path, and the one most people will use:
- Open the Web UI (
npx @deepseek-ai/dsh web, defaulthttp://127.0.0.1:3080). - Go to Settings → Models.
- Find the DeepSeek card — it has one field, the API key.
- Paste your key, save.
That's it. The change takes effect on your next request; you don't need to restart the dsh process.
Credential resolution order
If you'd rather not store the key through the UI at all — for example, in a CI job or a container — dsh resolves credentials from four sources, checked in this order:
1. Inherited process environment
2. $DSH_HOME/.credentials.yaml
3. .env in the invoking directory
4. $DSH_HOME/.env
One detail worth knowing: managed credentials that go through .credentials.yaml are never written into process.env — that file is a separate, dedicated credential store, not just another environment layer. The two .env files, by contrast, are ordinary startup environment layers, checked lower in precedence than a credential already resolved by the app.
A concrete example: to skip the UI entirely and set your key by environment variable for a headless/CI run, export it before invoking dsh:
export DEEPSEEK_API_KEY=your-key-here
dsh --profile headless "summarize the open pull requests"
Note that DEEPSEEK_API_KEY specifically is documented as the credential for the base bundle's web_search tool (DeepSeek's native search) and is also the default variable name the Python SDK's example scripts read for model auth — it is not necessarily the same variable dsh's model-routing layer reads for every provider, so check settings.yaml if you're wiring up a non-default provider this way.
Adding a non-DeepSeek provider
If you want to use Anthropic, OpenAI, or another provider dsh ships a built-in directory entry for, choose Add provider in Settings → Models instead of using the DeepSeek card. Fill in the API key and you're generally done — the endpoint, protocol, and model list are already baked in for directory providers.
A few providers need more than an API key because they use provider-native auth instead of a bearer token:
| Provider | What it actually needs |
|---|---|
| Bedrock | AWS credentials |
| Vertex | Google Application Default Credentials + project |
| Azure | An api-version in addition to the key |
| Codex | OAuth, not a static API key |
For anything outside dsh's built-in directory — a self-hosted model gateway, a company-internal proxy, or any OpenAI-compatible endpoint — see Using OpenAI, Anthropic, or Any OpenAI-Compatible API with DeepSeek Harness for the full custom-provider walkthrough, including how to declare image-input support for vision models.
Credentials are shared across every profile
It's worth being explicit about scope: model provider configuration lives under $DSH_HOME, not inside any individual profile directory. That means if you run multiple profiles — say a web profile for interactive work and a separate ci-headless profile for scripted runs — they all read the same settings.yaml and .credentials.yaml. You configure a provider once, and every profile you launch afterward can use it; there's no per-profile credential duplication to keep in sync. Profile-scoped configuration (which plugins are active, tool defaults, sandbox tweaks) is a separate layer entirely, written to that profile's own cordis.patch.yml.
This also means a leaked or over-broad credential isn't contained to one profile — if you're running dsh across several profiles with meaningfully different trust levels, keep that shared-credential scope in mind.
Verifying your setup worked
After saving a key, the fastest sanity check is a trivial headless call, since it prints the result directly to your terminal without needing the Web UI at all:
dsh --profile headless "reply with the word 'ok' and nothing else"
If that returns ok and exits 0, your model provider and credentials are wired up correctly end to end. If it fails, the error text will point at either MISSING_CREDENTIAL or UNKNOWN_MODEL — see the table below for what each one means and how to fix it.
Common errors and fixes
| Error | Cause / fix |
|---|---|
MISSING_CREDENTIAL | No key resolved for the selected model/provider — set it via Settings → Models, or supply it through one of the credential-resolution sources above |
UNKNOWN_MODEL | You've selected a model ID that isn't registered anywhere — either pick an already-configured model, or add the missing model ID to your custom provider's model list |
| Fetch available models returns 401 | Your API key is invalid for that endpoint. This button calls the OpenAI-compatible GET /models endpoint specifically — if your provider doesn't implement it, you'll need to type in model IDs by hand instead |
| An attached image gets rejected before sending | The model you're using hasn't declared input: [text, image] — see the custom providers guide for the exact YAML |
FAQ
Where exactly does DeepSeek Harness store my API key?
In $DSH_HOME/.credentials.yaml (default $DSH_HOME is ~/.dsh), a file kept separate from the non-sensitive settings.yaml. The Web UI never displays the plaintext key back to you after you save it — only a masked reference.
Do I need to restart dsh after changing my API key?
No. Saving a key through Settings → Models takes effect on the next request.
Can I set my API key with an environment variable instead of the Web UI?
Yes — dsh checks the inherited process environment first in its credential resolution order, ahead of .credentials.yaml and both .env locations. This is the typical path for CI and headless automation.
What does DEEPSEEK_API_KEY specifically control?
It's documented as the credential for the base bundle's native web_search tool, and is the variable name the Python SDK's example scripts read by default for model auth. For other providers or custom endpoints, check what variable name your settings.yaml entry actually references.
I configured a provider but get UNKNOWN_MODEL — what's wrong?
The model ID you selected isn't registered in settings.yaml for any configured provider. Either switch to a model that is configured, or add the missing model ID under that provider's models list — see Using OpenAI, Anthropic, or Any OpenAI-Compatible API with DeepSeek Harness for the YAML format.
Next steps
- DeepSeek Harness Quickstart — the full path from install to first session.
- Using OpenAI, Anthropic, or Any OpenAI-Compatible API with DeepSeek Harness — custom endpoints, vision models, and provider auth details.
- DeepSeek Harness Web UI Explained — the rest of the Settings page and session flow.
- Browse provider-related plugins in Models & Providers, including wallet/cost-tracking tools like dsh-codex-connect.