---
title: Configuration
path: reference/configuration
status: published
---

# Configuration

Environment variables read by the ScaiFlow API backend. Loaded via `pydantic-settings` from (in increasing-precedence order):

1. `/etc/scaiflow/api.env` (legacy fallback)
2. `./.env` (CWD-relative, Vite-style)
3. `apps/api/.env` (package-relative, canonical for the API)
4. `$SCAIFLOW_ENV_FILE` (explicit override path)
5. Process environment variables (highest precedence)

Real env vars always override file contents.

## API listener

| Var | Default | Notes |
|---|---|---|
| `SCAIFLOW_API_HOST` | `127.0.0.1` | Bind address. Use `0.0.0.0` to listen on all interfaces. |
| `SCAIFLOW_API_PORT` | `8001` | Listen port. |
| `SCAIFLOW_API_RELOAD` | `false` | Truthy → uvicorn auto-reload. Dev only. |

## Database

| Var | Default | Notes |
|---|---|---|
| `SCAIFLOW_DB_URL` | `sqlite+aiosqlite:///./scaiflow.db` | SQLAlchemy async URL. Prod: `mysql+aiomysql://user:pass@host/scaiflow`. |
| `SCAIFLOW_SKIP_CREATE_ALL` | `false` | Prod: set to `true` — Alembic owns schema. Dev/sqlite/tests: leave `false` for the bootstrap `create_all`. |

## ScaiKey (OIDC)

| Var | Default | Notes |
|---|---|---|
| `SCAIKEY_BASE_URL` | `null` | ScaiKey hostname, e.g. `https://scaikey.scailabs.ai`. |
| `SCAIKEY_AUDIENCE` | `null` | Override expected `aud` claim. Falls back to `SCAIFLOW_SCAIKEY_CLIENT_ID` when unset. |
| `SCAIFLOW_SCAIKEY_CLIENT_ID` | `null` | The OIDC application's client id (shared by canvas + BFF). |
| `SCAIFLOW_SCAIKEY_CLIENT_SECRET` | `null` | Server-side only. Never sent to the canvas. |
| `SCAIFLOW_SCAIKEY_APPLICATION_ID` | `me` | App identifier used by the effective_users sync endpoint. Default `me` resolves to the app the credentials belong to. |
| `SCAIFLOW_SCAIKEY_WEBHOOK_SECRET` | `null` | HMAC-SHA256 secret for verifying inbound webhook signatures at `/v1/webhooks/scaikey`. Unset → webhook endpoint returns 503 (fail-closed). |
| `SCAIFLOW_AUTH_DISABLED` | `false` | Dev shortcut: returns a fixed dev principal regardless of bearer. NEVER enable in prod. |

## Sync

| Var | Default | Notes |
|---|---|---|
| `SCAIFLOW_SYNC_INTERVAL_MINUTES` | `60` | How often the background loop syncs users/groups from ScaiKey. |
| `SCAIFLOW_DISABLE_BACKGROUND_SYNC` | `false` | When `true`, the hourly loop doesn't run. Manual sync via `POST /v1/admin/sync` still works. |
| `SCAIFLOW_SUPER_ADMIN_GROUPS` | `""` | Comma-separated ScaiKey group ids OR names. Members of any listed group auto-elevate to super_admin (`source=group`). Removed → auto-demoted. |

## ScaiGrid

| Var | Default | Notes |
|---|---|---|
| `SCAIGRID_BASE_URL` | `null` | Defaults to the SDK's built-in public endpoint. |
| `SCAIGRID_API_KEY` | `null` | Server-to-server fallback when no per-tenant key is set and no user JWT is available. Dev only. |
| `SCAIFLOW_DEPLOY_AUTH` | `null` | Pin to `user_jwt` or `api_key` for debugging. Unset → default precedence (JWT first, then per-tenant key). |

## Object storage (flow content)

| Var | Default | Notes |
|---|---|---|
| `SCAIFLOW_STORAGE_BACKEND` | `filesystem` | `filesystem` (dev) or `s3` (prod / S3-compatible). |
| `SCAIFLOW_STORAGE_PATH` | `./var/flows` | Filesystem backend: root dir for `flows/{tenant}/{flow}/v{version}.flow.json`. |
| `SCAIFLOW_STORAGE_S3_BUCKET` | `null` | S3 backend: bucket name. |
| `SCAIFLOW_STORAGE_S3_REGION` | `null` | S3 region. |
| `SCAIFLOW_STORAGE_S3_ENDPOINT` | `null` | Custom S3-compatible endpoint (Storey, MinIO, etc.). Leave unset for AWS S3 default. |
| `AWS_REGION` | `null` | Standard AWS env. |
| `AWS_ACCESS_KEY_ID` | `null` | Standard AWS env. |
| `AWS_SECRET_ACCESS_KEY` | `null` | Standard AWS env. |

## Legacy

| Var | Default | Notes |
|---|---|---|
| `SCAIFLOW_API_TOKEN` | `null` | Shared bearer for `POST /v1/dev/deploy` (deprecated). Drop once canvas is fully ScaiKey-OIDC. |

## Reading current values

```python
from scaiflow_api.settings import settings
print(settings.scaikey_base_url, settings.deploy_auth)
```

`settings` is a lazy proxy — first attribute access constructs the Pydantic `Settings` from the resolved env/file values. Tests can reset it via `settings.reset()`.

## A minimal `.env` for dev

```dotenv
SCAIFLOW_DB_URL=sqlite+aiosqlite:///./scaiflow.db
SCAIFLOW_API_TOKEN=dev-token-changeme
SCAIGRID_BASE_URL=https://scaigrid.scailabs.ai
SCAIGRID_API_KEY=sgk_dev_xxx
```

ScaiKey is optional in dev — the canvas falls back to dev-token mode when `SCAIKEY_BASE_URL` is unset.

## A minimal `.env` for prod

```dotenv
SCAIFLOW_API_HOST=0.0.0.0
SCAIFLOW_API_PORT=8001
SCAIFLOW_DB_URL=mysql+aiomysql://user:pass@db.example/scaiflow
SCAIFLOW_SKIP_CREATE_ALL=true

SCAIKEY_BASE_URL=https://scaikey.scailabs.ai
SCAIFLOW_SCAIKEY_CLIENT_ID=...
SCAIFLOW_SCAIKEY_CLIENT_SECRET=...
SCAIFLOW_SCAIKEY_WEBHOOK_SECRET=...
SCAIFLOW_SUPER_ADMIN_GROUPS=grp_scaiflow_admins

SCAIFLOW_STORAGE_BACKEND=s3
SCAIFLOW_STORAGE_S3_BUCKET=scaiflow-prod
SCAIFLOW_STORAGE_S3_REGION=eu-west-1
SCAIFLOW_STORAGE_S3_ENDPOINT=https://storey-api.ech1.intranet.example
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...
```

Do **not** set `SCAIGRID_API_KEY` in prod — use per-tenant keys via `POST /v1/tenants/me/scaigrid_credentials`.
