Configuration
OpenClaw reads an optional JSON5 config from ~/.openclaw/openclaw.json.
If the file is missing, OpenClaw uses safe defaults. Common reasons to add a config:
- Connect channels and control who can message the bot
- Set models, tools, sandboxing, or automation (cron, hooks)
- Tune sessions, media, networking, or UI
See the full reference for every available field.
π‘ Tip:
New to configuration? Start with
openclaw onboardfor interactive setup, or check out the Configuration Examples guide for complete copy-paste configs.
Minimal config
// ~/.openclaw/openclaw.json
{
agents: { defaults: { workspace: "~/.openclaw/workspace" } },
channels: { whatsapp: { allowFrom: ["+15555550123"] } },
}
Editing config
Interactive wizard:
openclaw onboard # full setup wizard
openclaw configure # config wizard
```
**CLI (one-liners):**
```bash
openclaw config get agents.defaults.workspace
openclaw config set agents.defaults.heartbeat.every "2h"
openclaw config unset tools.web.search.apiKey
```
**Control UI:**
Open [http://127.0.0.1:18789](http://127.0.0.1:18789) and use the **Config** tab.
The Control UI renders a form from the config schema, with a **Raw JSON** editor as an escape hatch.
**Direct edit:**
Edit `~/.openclaw/openclaw.json` directly. The Gateway watches the file and applies changes automatically (see [hot reload](#config-hot-reload)).
## Strict validation
> **β οΈ Warning:**
>
> OpenClaw only accepts configurations that fully match the schema. Unknown keys, malformed types, or invalid values cause the Gateway to **refuse to start**. The only root-level exception is `$schema` (string), so editors can attach JSON Schema metadata.
When validation fails:
- The Gateway does not boot
- Only diagnostic commands work (`openclaw doctor`, `openclaw logs`, `openclaw health`, `openclaw status`)
- Run `openclaw doctor` to see exact issues
- Run `openclaw doctor --fix` (or `--yes`) to apply repairs
## Common tasks
<details>
<summary>Set up a channel (WhatsApp, Telegram, Discord, etc.)</summary>
Each channel has its own config section under `channels.<provider>`. See the dedicated channel page for setup steps:
- [WhatsApp](./channels/whatsapp.md) β `channels.whatsapp`
- [Telegram](./channels/telegram.md) β `channels.telegram`
- [Discord](./channels/discord.md) β `channels.discord`
- [Slack](./channels/slack.md) β `channels.slack`
- [Signal](./channels/signal.md) β `channels.signal`
- [iMessage](./channels/imessage.md) β `channels.imessage`
- [Google Chat](./channels/googlechat.md) β `channels.googlechat`
- [Mattermost](./channels/mattermost.md) β `channels.mattermost`
- [MS Teams](./channels/msteams.md) β `channels.msteams`
All channels share the same DM policy pattern:
```json5
{
channels: {
telegram: {
enabled: true,
botToken: "123:abc",
dmPolicy: "pairing", // pairing | allowlist | open | disabled
allowFrom: ["tg:123"], // only for allowlist/open
},
},
}
```
</details>
<details>
<summary>Choose and configure models</summary>
Set the primary model and optional fallbacks:
```json5
{
agents: {
defaults: {
model: {
primary: "anthropic/claude-sonnet-4-5",
fallbacks: ["openai/gpt-5.2"],
},
models: {
"anthropic/claude-sonnet-4-5": { alias: "Sonnet" },
"openai/gpt-5.2": { alias: "GPT" },
},
},
},
}
```
- `agents.defaults.models` defines the model catalog and acts as the allowlist for `/model`.
- Model refs use `provider/model` format (e.g. `anthropic/claude-opus-4-6`).
- See [Models CLI](./concepts/models.md) for switching models in chat and [Model Failover](./concepts/model-failover.md) for auth rotation and fallback behavior.
- For custom/self-hosted providers, see [Custom providers](./gateway/configuration-reference#custom-providers-and-base-urls.md) in the reference.
</details>
<details>
<summary>Control who can message the bot</summary>
DM access is controlled per channel via `dmPolicy`:
- `"pairing"` (default): unknown senders get a one-time pairing code to approve
- `"allowlist"`: only senders in `allowFrom` (or the paired allow store)
- `"open"`: allow all inbound DMs (requires `allowFrom: ["*"]`)
- `"disabled"`: ignore all DMs
For groups, use `groupPolicy` + `groupAllowFrom` or channel-specific allowlists.
See the [full reference](./gateway/configuration-reference#dm-and-group-access.md) for per-channel details.
</details>
<details>
<summary>Set up group chat mention gating</summary>
Group messages default to **require mention**. Configure patterns per agent:
```json5
{
agents: {
list: [
{
id: "main",
groupChat: {
mentionPatterns: ["@openclaw", "openclaw"],
},
},
],
},
channels: {
whatsapp: {
groups: { "*": { requireMention: true } },
},
},
}
```
- **Metadata mentions**: native @-mentions (WhatsApp tap-to-mention, Telegram @bot, etc.)
- **Text patterns**: regex patterns in `mentionPatterns`
- See [full reference](./gateway/configuration-reference#group-chat-mention-gating.md) for per-channel overrides and self-chat mode.
</details>
<details>
<summary>Configure sessions and resets</summary>
Sessions control conversation continuity and isolation:
```json5
{
session: {
dmScope: "per-channel-peer", // recommended for multi-user
reset: {
mode: "daily",
atHour: 4,
idleMinutes: 120,
},
},
}
```
- `dmScope`: `main` (shared) | `per-peer` | `per-channel-peer` | `per-account-channel-peer`
- See [Session Management](./concepts/session.md) for scoping, identity links, and send policy.
- See [full reference](./gateway/configuration-reference#session.md) for all fields.
</details>
<details>
<summary>Enable sandboxing</summary>
Run agent sessions in isolated Docker containers:
```json5
{
agents: {
defaults: {
sandbox: {
mode: "non-main", // off | non-main | all
scope: "agent", // session | agent | shared
},
},
},
}
```
Build the image first: `scripts/sandbox-setup.sh`
See [Sandboxing](./gateway/sandboxing.md) for the full guide and [full reference](./gateway/configuration-reference#sandbox.md) for all options.
</details>
<details>
<summary>Set up heartbeat (periodic check-ins)</summary>
```json5
{
agents: {
defaults: {
heartbeat: {
every: "30m",
target: "last",
},
},
},
}
```
- `every`: duration string (`30m`, `2h`). Set `0m` to disable.
- `target`: `last` | `whatsapp` | `telegram` | `discord` | `none`
- See [Heartbeat](./gateway/heartbeat.md) for the full guide.
</details>
<details>
<summary>Configure cron jobs</summary>
```json5
{
cron: {
enabled: true,
maxConcurrentRuns: 2,
sessionRetention: "24h",
},
}
```
See [Cron jobs](./automation/cron-jobs.md) for the feature overview and CLI examples.
</details>
<details>
<summary>Set up webhooks (hooks)</summary>
Enable HTTP webhook endpoints on the Gateway:
```json5
{
hooks: {
enabled: true,
token: "shared-secret",
path: "/hooks",
defaultSessionKey: "hook:ingress",
allowRequestSessionKey: false,
allowedSessionKeyPrefixes: ["hook:"],
mappings: [
{
match: { path: "gmail" },
action: "agent",
agentId: "main",
deliver: true,
},
],
},
}
```
See [full reference](./gateway/configuration-reference#hooks.md) for all mapping options and Gmail integration.
</details>
<details>
<summary>Configure multi-agent routing</summary>
Run multiple isolated agents with separate workspaces and sessions:
```json5
{
agents: {
list: [
{ id: "home", default: true, workspace: "~/.openclaw/workspace-home" },
{ id: "work", workspace: "~/.openclaw/workspace-work" },
],
},
bindings: [
{ agentId: "home", match: { channel: "whatsapp", accountId: "personal" } },
{ agentId: "work", match: { channel: "whatsapp", accountId: "biz" } },
],
}
```
See [Multi-Agent](./concepts/multi-agent.md) and [full reference](./gateway/configuration-reference#multi-agent-routing.md) for binding rules and per-agent access profiles.
</details>
<details>
<summary>Split config into multiple files ($include)</summary>
Use `$include` to organize large configs:
```json5
// ~/.openclaw/openclaw.json
{
gateway: { port: 18789 },
agents: { $include: "./agents.json5" },
broadcast: {
$include: ["./clients/a.json5", "./clients/b.json5"],
},
}
```
- **Single file**: replaces the containing object
- **Array of files**: deep-merged in order (later wins)
- **Sibling keys**: merged after includes (override included values)
- **Nested includes**: supported up to 10 levels deep
- **Relative paths**: resolved relative to the including file
- **Error handling**: clear errors for missing files, parse errors, and circular includes
</details>
## Config hot reload
The Gateway watches `~/.openclaw/openclaw.json` and applies changes automatically β no manual restart needed for most settings.
### Reload modes
| Mode | Behavior |
| ---------------------- | --------------------------------------------------------------------------------------- |
| **`hybrid`** (default) | Hot-applies safe changes instantly. Automatically restarts for critical ones. |
| **`hot`** | Hot-applies safe changes only. Logs a warning when a restart is needed β you handle it. |
| **`restart`** | Restarts the Gateway on any config change, safe or not. |
| **`off`** | Disables file watching. Changes take effect on the next manual restart. |
```json5
{
gateway: {
reload: { mode: "hybrid", debounceMs: 300 },
},
}
What hot-applies vs what needs a restart
Most fields hot-apply without downtime. In hybrid mode, restart-required changes are handled automatically.
| Category | Fields | Restart needed? |
|---|---|---|
| Channels | channels.*, web (WhatsApp) β all built-in and extension channels | No |
| Agent & models | agent, agents, models, routing | No |
| Automation | hooks, cron, agent.heartbeat | No |
| Sessions & messages | session, messages | No |
| Tools & media | tools, browser, skills, audio, talk | No |
| UI & misc | ui, logging, identity, bindings | No |
| Gateway server | gateway.* (port, bind, auth, tailscale, TLS, HTTP) | Yes |
| Infrastructure | discovery, canvasHost, plugins | Yes |
π Note:
gateway.reloadandgateway.remoteare exceptions β changing them does not trigger a restart.
Config RPC (programmatic updates)
config.apply (full replace)
Validates + writes the full config and restarts the Gateway in one step.
> **β οΈ Warning:**
config.applyreplaces the entire config. Useconfig.patchfor partial updates, oropenclaw config setfor single keys.
Params:
- `raw` (string) β JSON5 payload for the entire config
- `baseHash` (optional) β config hash from `config.get` (required when config exists)
- `sessionKey` (optional) β session key for the post-restart wake-up ping
- `note` (optional) β note for the restart sentinel
- `restartDelayMs` (optional) β delay before restart (default 2000)
```bash
openclaw gateway call config.get --params '{}' # capture payload.hash
openclaw gateway call config.apply --params '{
"raw": "{ agents: { defaults: { workspace: \"~/.openclaw/workspace\" } } }",
"baseHash": "<hash>",
"sessionKey": "agent:main:whatsapp:dm:+15555550123"
}'
```
config.patch (partial update)
Merges a partial update into the existing config (JSON merge patch semantics):
- Objects merge recursively
- `null` deletes a key
- Arrays replace
Params:
- `raw` (string) β JSON5 with just the keys to change
- `baseHash` (required) β config hash from `config.get`
- `sessionKey`, `note`, `restartDelayMs` β same as `config.apply`
```bash
openclaw gateway call config.patch --params '{
"raw": "{ channels: { telegram: { groups: { \"*\": { requireMention: false } } } } }",
"baseHash": "<hash>"
}'
```
Environment variables
OpenClaw reads env vars from the parent process plus:
.envfrom the current working directory (if present)~/.openclaw/.env(global fallback)
Neither file overrides existing env vars. You can also set inline env vars in config:
{
env: {
OPENROUTER_API_KEY: "sk-or-...",
vars: { GROQ_API_KEY: "gsk-..." },
},
}
Shell env import (optional)
If enabled and expected keys arenβt set, OpenClaw runs your login shell and imports only the missing keys:
{
env: {
shellEnv: { enabled: true, timeoutMs: 15000 },
},
}
Env var equivalent: OPENCLAW_LOAD_SHELL_ENV=1
Env var substitution in config values
Reference env vars in any config string value with ${VAR_NAME}:
{
gateway: { auth: { token: "${OPENCLAW_GATEWAY_TOKEN}" } },
models: { providers: { custom: { apiKey: "${CUSTOM_API_KEY}" } } },
}
Rules:
- Only uppercase names matched:
[A-Z_][A-Z0-9_]* - Missing/empty vars throw an error at load time
- Escape with
$${VAR}for literal output - Works inside
$includefiles - Inline substitution:
"${BASE}/v1"β"https://api.example.com/v1"
See Environment for full precedence and sources.
Full reference
For the complete field-by-field reference, see Configuration Reference.
Related: Configuration Examples Β· Configuration Reference Β· Doctor