# Secrets

> Secrets allow your app to securely access API keys and sensitive values. Users provide their own secrets which are encrypted and injected at runtime.

**URL:** https://inference.sh/docs/extend/secrets
**Last updated:** 2026-10-12

---

Secrets allow your app to securely access API keys and sensitive values. Users provide their own secrets which are encrypted and injected at runtime.

---

## Declaring Secrets

Define required secrets in `inf.yml`:

```yaml
secrets:
  - key: API_KEY
    description: Your API key for the external service
    optional: false

  - key: WEBHOOK_SECRET
    description: Optional webhook signing secret
    optional: true
```

### Properties

| Property | Type | Description |
|----------|------|-------------|
| `key` | string | Environment variable name |
| `description` | string | Shown to users |
| `optional` | boolean | If false, app won't run without it |

### Keys that belong to a service

Declare an API key for a service (Acme, OpenAI, fal) under [`credentials:`](/docs/extend/credentials#api-keys-from-a-provider) with its provider instead:

```yaml
credentials:
  - provider: acme
    secrets: [ACME_API_KEY]
```

Your app reads `ACME_API_KEY` the same way. Users see which service the key is for, and the key they paste is saved as that provider's credential, so other apps that need Acme find it too. Use `secrets:` for values that belong to no service.

Secret `key` values must not use [names reserved for platform settings](/docs/api/rest/secrets#reserved-platform-keys). Users cannot store those names as workspace secrets.

When a declared `key` matches exactly one **`api_key`** provider's standard API key name (for example a model provider's documented env var), saving the value through the vault, CLI, or API links it to that provider's credential even though the app lists it under `secrets:` rather than `credentials:`. OAuth app credential names are never inferred this way. Use a custom key name when you want a plain environment secret with no provider connection.

---

## Accessing Secrets

Secrets are injected as environment variables:

<div data-type="codegroup">

```python Python
import os

class App(BaseApp):
    async def setup(self, config):
        self.api_key = os.environ.get("API_KEY")
        self.webhook_secret = os.environ.get("WEBHOOK_SECRET")
```

```javascript Node.js
export class App {
  async setup(config) {
    this.apiKey = process.env.API_KEY;
    this.webhookSecret = process.env.WEBHOOK_SECRET;
  }
}
```

</div>

---

## Common Patterns

### External API Access

```yaml
secrets:
  - key: OPENAI_API_KEY
    description: OpenAI API key for GPT models
    optional: false
```

<div data-type="codegroup">

```python Python
from openai import OpenAI

class App(BaseApp):
    async def setup(self, config):
        self.client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
```

```javascript Node.js
import OpenAI from "openai";

export class App {
  async setup(config) {
    this.client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
  }
}
```

</div>

### HuggingFace Token

```yaml
secrets:
  - key: HF_TOKEN
    description: HuggingFace token for gated models
    optional: false
```

Add `huggingface_hub` to `requirements.txt`; it is not preinstalled.

```python
from huggingface_hub import snapshot_download

self.model_path = snapshot_download(
    repo_id="meta-llama/Llama-2-7b",
    token=os.environ.get("HF_TOKEN")
)
```

---

## Managing Secrets via CLI

Users can manage their secrets using the CLI:

```bash
# List all secrets
belt secrets list

# Set a secret (creates or updates; prompts for the value without echo)
belt secrets set OPENAI_API_KEY

# Pipe the value in scripts (keeps it out of shell history and ps)
printf %s "$OPENAI_API_KEY" | belt secrets set OPENAI_API_KEY -d "For GPT-4 calls"

# Save as a provider credential (apps declaring that provider receive the key)
belt secrets set ACME_API_KEY --provider acme --provider-name "Acme CRM"

# Attach or detach without re-entering the value
belt secrets attach GRAFANA_API_KEY grafana
belt secrets detach GRAFANA_API_KEY

# Get a secret (shows masked value)
belt secrets get OPENAI_API_KEY

# Delete a secret
belt secrets delete OPENAI_API_KEY
```

---

## Best Practices

1. **Clear descriptions** - help users understand what each secret is for
2. **Mark optional correctly** - only if the app truly works without it
3. **Don't log secrets** - never print or log secret values
4. **Validate early** - check in `setup()` and fail fast
5. **Use specific names** - `OPENAI_API_KEY` is better than `API_KEY`

---

## REST API

Programmatic CRUD for workspace secrets: [Secrets API](/docs/api/rest/secrets).

---

## Next

→ [Credentials](/docs/extend/credentials) - OAuth service connections
