Skip to main content

Configuration and operations

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.

💡 New to configuration? Start with openclaw onboard for 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

openclaw onboard # full setup wizard
openclaw configure # config wizard
openclaw config get agents.defaults.workspace
openclaw config set agents.defaults.heartbeat.every "2h"
openclaw config unset tools.web.search.apiKey

Open 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.

Edit ~/.openclaw/openclaw.json directly. The Gateway watches the file and applies changes automatically (see hot reload).

Strict validation

⚠️ 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

Each channel has its own config section under channels.. See the dedicated channel page for setup steps:

All channels share the same DM policy pattern:

{
channels: {
telegram: {
enabled: true,
botToken: "123:abc",
dmPolicy: "pairing", // pairing | allowlist | open | disabled
allowFrom: ["tg:123"], // only for allowlist/open
},
},
}

Set the primary model and optional fallbacks:

{
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).
  • agents.defaults.imageMaxDimensionPx controls transcript/tool image downscaling (default 1200); lower values usually reduce vision-token usage on screenshot-heavy runs.
  • See Models CLI for switching models in chat and Model Failover for auth rotation and fallback behavior.
  • For custom/self-hosted providers, see Custom providers in the reference.

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 for per-channel details.

Group messages default to require mention. Configure patterns per agent:

{
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 for per-channel overrides and self-chat mode.

Sessions control conversation continuity and isolation:

{
session: {
dmScope: "per-channel-peer", // recommended for multi-user
threadBindings: {
enabled: true,
idleHours: 24,
maxAgeHours: 0,
},
reset: {
mode: "daily",
atHour: 4,
idleMinutes: 120,
},
},
}
  • dmScope: main (shared) | per-peer | per-channel-peer | per-account-channel-peer
  • threadBindings: global defaults for thread-bound session routing (Discord supports /focus, /unfocus, /agents, /session idle, and /session max-age).
  • See Session Management for scoping, identity links, and send policy.
  • See full reference for all fields.

Run agent sessions in isolated Docker containers:

{
agents: {
defaults: {
sandbox: {
mode: "non-main", // off | non-main | all
scope: "agent", // session | agent | shared
},
},
},
}

Build the image first: scripts/sandbox-setup.shSee Sandboxing for the full guide and full reference for all options.

{
agents: {
defaults: {
heartbeat: {
every: "30m",
target: "last",
},
},
},
}
  • every: duration string (30m, 2h). Set 0m to disable.
  • target: last | whatsapp | telegram | discord | none
  • directPolicy: allow (default) or block for DM-style heartbeat targets
  • See Heartbeat for the full guide.
{
cron: {
enabled: true,
maxConcurrentRuns: 2,
sessionRetention: "24h",
runLog: {
maxBytes: "2mb",
keepLines: 2000,
},
},
}
  • sessionRetention: prune completed isolated run sessions from sessions.json (default 24h; set false to disable).
  • runLog: prune cron/runs/.jsonl by size and retained lines.
  • See Cron jobs for feature overview and CLI examples.

Enable HTTP webhook endpoints on the Gateway:

{
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,
},
],
},
}

Security note:

  • Treat all hook/webhook payload content as untrusted input.
  • Keep unsafe-content bypass flags disabled (hooks.gmail.allowUnsafeExternalContent, hooks.mappings[].allowUnsafeExternalContent) unless doing tightly scoped debugging.
  • For hook-driven agents, prefer strong modern model tiers and strict tool policy (for example messaging-only plus sandboxing where possible).

See full reference for all mapping options and Gmail integration.

Run multiple isolated agents with separate workspaces and sessions:

{
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 and full reference for binding rules and per-agent access profiles.

Use $include to organize large configs:

// ~/.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

Config hot reload

The Gateway watches ~/.openclaw/openclaw.json and applies changes automatically — no manual restart needed for most settings.

Reload modes

ModeBehavior
hybrid (default)Hot-applies safe changes instantly. Automatically restarts for critical ones.
hotHot-applies safe changes only. Logs a warning when a restart is needed — you handle it.
restartRestarts the Gateway on any config change, safe or not.
offDisables file watching. Changes take effect on the next manual restart.
{
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.

CategoryFieldsRestart needed?
Channelschannels.*, web (WhatsApp) — all built-in and extension channelsNo
Agent & modelsagent, agents, models, routingNo
Automationhooks, cron, agent.heartbeatNo
Sessions & messagessession, messagesNo
Tools & mediatools, browser, skills, audio, talkNo
UI & miscui, logging, identity, bindingsNo
Gateway servergateway.* (port, bind, auth, tailscale, TLS, HTTP)Yes
Infrastructurediscovery, canvasHost, pluginsYes

ℹ️ gateway.reload and gateway.remote are exceptions — changing them does not trigger a restart.

Config RPC (programmatic updates)

ℹ️ Control-plane write RPCs (config.apply, config.patch, update.run) are rate-limited to 3 requests per 60 seconds per deviceId+clientIp. When limited, the RPC returns UNAVAILABLE with retryAfterMs.

Validates + writes the full config and restarts the Gateway in one step.

config.apply replaces the entire config. Use config.patch for partial updates, or openclaw config set for 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)

Restart requests are coalesced while one is already pending/in-flight, and a 30-second cooldown applies between restart cycles.

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"
}'

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

Restart behavior matches config.apply: coalesced pending restarts plus a 30-second cooldown between restart cycles.

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:

  • .env from 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-..." },
},
}

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

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 $include files
  • Inline substitution: "${BASE}/v1""https://api.example.com/v1"

For fields that support SecretRef objects, you can use:

{
models: {
providers: {
openai: { apiKey: { source: "env", provider: "default", id: "OPENAI_API_KEY" } },
},
},
skills: {
entries: {
"nano-banana-pro": {
apiKey: {
source: "file",
provider: "filemain",
id: "/skills/entries/nano-banana-pro/apiKey",
},
},
},
},
channels: {
googlechat: {
serviceAccountRef: {
source: "exec",
provider: "vault",
id: "channels/googlechat/serviceAccount",
},
},
},
}

SecretRef details (including secrets.providers for env/file/exec) are in Secrets Management. Supported credential paths are listed in SecretRef Credential Surface.

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

Gateway RunbookConfiguration Reference