OpenClaw Config Reference: Every openclaw.json Option
OpenClaw was previously known as Clawdbot and Moltbot. This guide applies to all versions.
OpenClaw environment variables and openclaw.json config keys organized by category: gateway, channels, agents, session, cron, tools, and security.
Key takeaways
- Every OpenClaw config lives in
~/.openclaw/openclaw.json(JSON5 format: comments and trailing commas allowed) - The schema is strict. Any unknown key causes the gateway to refuse to start. Run
openclaw doctorto diagnose failures. - Sensitive credentials like bot tokens have environment variable fallbacks so you can keep secrets out of the config file
- You can edit config with
openclaw config set <key> <value>, the interactive wizard (openclaw configure), or the Control UI athttp://127.0.0.1:18789 - All fields are optional. OpenClaw uses safe defaults when a key is omitted
Fixes when it breaks. Workflows when it doesn't.
OpenClaw guides, configs, and troubleshooting notes. Every two weeks.
What openclaw.json is and where to find it
The config file lives at ~/.openclaw/openclaw.json. If the file does not exist, OpenClaw boots with safe defaults. The format is JSON5, which means you can use comments (// or /* */) and trailing commas without breaking the parser.
OpenClaw validates the file against a strict schema on startup. Unknown keys, wrong types, and invalid values all prevent the gateway from starting. Only the root-level $schema key is exempt, so JSON Schema-aware editors can attach metadata. When validation fails, diagnostic commands like openclaw doctor, openclaw logs, openclaw health, and openclaw status continue to work so you can debug.
Run openclaw doctor --fix to auto-repair common issues.
How to edit the openclaw.json config
Four methods work for editing config:
CLI (best for scripting):
openclaw config get agents.defaults.workspace
openclaw config set agents.defaults.heartbeat.every "2h"
openclaw config unset plugins.entries.brave.config.webSearch.apiKeyInteractive wizard:
openclaw configure
openclaw onboardControl UI: Open http://127.0.0.1:18789 and use the Config tab. The UI renders a form from the schema with a Raw JSON editor as a fallback.
Direct file edit: Edit ~/.openclaw/openclaw.json directly. The gateway watches the file and applies changes automatically without a restart (hot reload).
Environment variable fallbacks for API keys
These environment variables are read automatically without needing entries in openclaw.json. Useful for secrets management or CI environments.
Model provider API key resolution
OpenClaw checks these sources in priority order for each provider:
| Priority | Variable | Notes |
|---|---|---|
| 1 | OPENCLAW_LIVE_<PROVIDER>_KEY | Single override, highest priority |
| 2 | <PROVIDER>_API_KEYS | Comma-separated list for rotation |
| 3 | <PROVIDER>_API_KEY | Standard single key |
| 4 | <PROVIDER>_API_KEY_* | Wildcard pattern, lowest priority |
Examples: ANTHROPIC_API_KEY, OPENAI_API_KEY, OPENROUTER_API_KEY. Google providers also check GOOGLE_API_KEY as a fallback.
OpenClaw only retries with the next key for rate-limit errors (429, quota, resource exhausted). Non-rate-limit errors are not retried with alternate keys.
For daemon setups (systemd, launchd), put keys in ~/.openclaw/.env so the daemon can read them without shell inheritance.
Channel-specific environment variables
| Variable | Config key it maps to |
|---|---|
TELEGRAM_BOT_TOKEN | channels.telegram.botToken (default account) |
DISCORD_BOT_TOKEN | channels.discord.token (default account) |
SLACK_BOT_TOKEN | channels.slack.botToken (default account) |
SLACK_APP_TOKEN | channels.slack.appToken (default account) |
GOOGLE_CHAT_SERVICE_ACCOUNT | channels.googlechat.serviceAccount |
GOOGLE_CHAT_SERVICE_ACCOUNT_FILE | channels.googlechat.serviceAccountFile |
iOS push relay variables (temporary overrides)
| Variable | Purpose |
|---|---|
OPENCLAW_APNS_RELAY_BASE_URL | Temporary override for relay base URL |
OPENCLAW_APNS_RELAY_TIMEOUT_MS | Timeout in ms (default: 10000) |
OPENCLAW_APNS_RELAY_ALLOW_HTTP=true | Dev-only loopback override. Do not persist. |
Gateway config options (gateway.*)
Controls the gateway server itself: binding, health checks, and push.
| Key | Type | Default | Description |
|---|---|---|---|
gateway.bind | string | 127.0.0.1 | Interface to bind. Use 0.0.0.0 to expose to the network |
gateway.port | number | 18789 | HTTP port for the gateway |
gateway.channelHealthCheckMinutes | number | 5 | How often the health monitor polls channels. Set to 0 to disable |
gateway.channelStaleEventThresholdMinutes | number | 30 | Minutes without events before a channel is considered stale. Should be >= check interval |
gateway.channelMaxRestartsPerHour | number | 10 | Max auto-restarts per channel per hour |
gateway.push.apns.relay.baseUrl | string | (none) | Relay URL for iOS push notifications on official builds |
gateway.push.apns.relay.timeoutMs | number | 10000 | Relay request timeout |
Disable auto-restarts globally:
{
gateway: {
channelHealthCheckMinutes: 0,
},
}Disable auto-restarts for one channel only:
{
channels: {
telegram: {
healthMonitor: { enabled: false },
},
},
}Channel config keys by provider
DM and group access policy (all channels)
Every channel supports dmPolicy and groupPolicy.
dmPolicy value | Behavior |
|---|---|
pairing (default) | Unknown senders get a one-time pairing code. Owner approves. Codes expire after 1 hour. Max 3 pending per channel. See dmPolicy docs. |
allowlist | Only senders in allowFrom or the paired allow store |
open | Allow all inbound DMs (requires allowFrom: ["*"]) |
disabled | Ignore all inbound DMs |
groupPolicy value | Behavior |
|---|---|
allowlist (default) | Only groups matching the configured allowlist |
open | Bypass group allowlists (mention-gating still applies) |
disabled | Block all group messages |
Use channels.defaults.groupPolicy to set a fallback for all providers at once.
Channel defaults (channels.defaults.*)
{
channels: {
defaults: {
groupPolicy: "allowlist",
heartbeat: {
showOk: false,
showAlerts: true,
useIndicator: true,
},
},
},
}| Key | Description |
|---|---|
channels.defaults.groupPolicy | Fallback group policy when a provider-level groupPolicy is unset |
channels.defaults.heartbeat.showOk | Include healthy channel statuses in heartbeat output |
channels.defaults.heartbeat.showAlerts | Include degraded/error statuses in heartbeat output |
channels.defaults.heartbeat.useIndicator | Render compact indicator-style heartbeat output |
Channel model overrides (channels.modelByChannel.*)
Pin specific channel IDs to a model. Applies when a session does not already have a model override.
{
channels: {
modelByChannel: {
telegram: {
"-1001234567890": "openai/gpt-4.1-mini",
},
discord: {
"123456789012345678": "anthropic/claude-opus-4-6",
},
},
},
}WhatsApp config (channels.whatsapp.*)
WhatsApp uses the Baileys Web protocol. It starts automatically when a linked session exists.
| Key | Type | Default | Description |
|---|---|---|---|
channels.whatsapp.dmPolicy | string | pairing | DM access policy |
channels.whatsapp.allowFrom | string[] | (none) | Phone numbers allowed to DM |
channels.whatsapp.textChunkLimit | number | 4000 | Max chars per message chunk |
channels.whatsapp.chunkMode | string | length | length or newline |
channels.whatsapp.mediaMaxMb | number | 50 | Max inbound media size |
channels.whatsapp.sendReadReceipts | boolean | true | Send blue read receipts |
channels.whatsapp.groupPolicy | string | allowlist | Group access policy |
channels.whatsapp.groupAllowFrom | string[] | (none) | Numbers allowed to use groups |
channels.whatsapp.groups.* | object | (none) | Per-group overrides. Use "*" for wildcard |
Multi-account: add account IDs under channels.whatsapp.accounts.<id>. Per-account keys: sendReadReceipts, dmPolicy, allowFrom.
Reconnect settings live under web.*:
| Key | Default | Description |
|---|---|---|
web.enabled | true | Enable Baileys web transport |
web.heartbeatSeconds | 60 | Keepalive interval |
web.reconnect.initialMs | 2000 | First reconnect delay |
web.reconnect.maxMs | 120000 | Max reconnect delay |
web.reconnect.factor | 1.4 | Exponential backoff factor |
web.reconnect.jitter | 0.2 | Jitter fraction |
web.reconnect.maxAttempts | 0 | 0 = unlimited |
Telegram config (channels.telegram.*)
| Key | Type | Default | Description |
|---|---|---|---|
channels.telegram.botToken | string | (none) | Bot token from BotFather. Env fallback: TELEGRAM_BOT_TOKEN |
channels.telegram.tokenFile | string | (none) | Path to token file (regular file only, no symlinks) |
channels.telegram.dmPolicy | string | pairing | DM access policy |
channels.telegram.allowFrom | string[] | (none) | Allowed user IDs (tg:123456789 format) |
channels.telegram.historyLimit | number | 50 | Messages to include in context |
channels.telegram.replyToMode | string | off | off, first, or all |
channels.telegram.linkPreview | boolean | true | Enable link previews |
channels.telegram.streaming | string | off | off, partial, block, or progress. Opt-in to avoid preview-edit rate limits |
channels.telegram.mediaMaxMb | number | 100 | Max inbound media |
channels.telegram.reactionNotifications | string | own | off, own, or all |
channels.telegram.proxy | string | (none) | SOCKS5 proxy URL |
channels.telegram.webhookUrl | string | (none) | Webhook URL (alternative to polling) |
channels.telegram.webhookSecret | string | (none) | Webhook validation secret |
channels.telegram.configWrites | boolean | true | Set false to block Telegram-initiated config writes |
channels.telegram.defaultAccount | string | (none) | Default account ID in multi-account setups |
Group config: channels.telegram.groups.<chatId>.* with keys allowFrom, systemPrompt, requireMention, topics.
Discord config (channels.discord.*)
| Key | Type | Default | Description |
|---|---|---|---|
channels.discord.token | string | (none) | Bot token. Env fallback: DISCORD_BOT_TOKEN |
channels.discord.dmPolicy | string | pairing | DM access policy |
channels.discord.allowFrom | string[] | (none) | Allowed user IDs |
channels.discord.allowBots | boolean/string | false | true, false, or "mentions" |
channels.discord.mediaMaxMb | number | 8 | Max media upload size |
channels.discord.historyLimit | number | 20 | Context message count |
channels.discord.textChunkLimit | number | 2000 | Max chars per message |
channels.discord.maxLinesPerMessage | number | 17 | Max lines before splitting (even under 2000 chars) |
channels.discord.replyToMode | string | off | off, first, or all |
channels.discord.streaming | string | off | off, partial, block, or progress |
channels.discord.defaultAccount | string | (none) | Default account in multi-account setups |
Thread bindings (channels.discord.threadBindings.*):
| Key | Default | Description |
|---|---|---|
enabled | true | Enable thread-bound session features |
idleHours | 24 | Auto-unfocus idle threads after this many hours. 0 disables |
maxAgeHours | 0 | Hard age cap for thread bindings. 0 disables |
spawnSubagentSessions | false | Opt-in for sessions_spawn({ thread: true }) auto thread creation |
Voice (channels.discord.voice.*):
| Key | Default | Description |
|---|---|---|
enabled | false | Enable voice channel conversations |
autoJoin | (none) | Array of {guildId, channelId} to auto-join |
daveEncryption | true | DAVE protocol encryption |
tts.provider | (none) | TTS provider for voice output |
Discord actions (channels.discord.actions.*):
All default to true except roles and moderation:
reactions, stickers, polls, permissions, messages, threads, pins, search, memberInfo, roleInfo, channelInfo, voiceStatus, events are all enabled by default.
roles and moderation are disabled by default (opt-in).
Per-guild config: channels.discord.guilds.<guildId>.* with keys slug, requireMention, ignoreOtherMentions, reactionNotifications, users, channels.
Slack config (channels.slack.*)
| Key | Type | Default | Description |
|---|---|---|---|
channels.slack.botToken | string | (none) | xoxb token. Env: SLACK_BOT_TOKEN |
channels.slack.appToken | string | (none) | xapp token for Socket Mode. Env: SLACK_APP_TOKEN |
channels.slack.signingSecret | string | (none) | Required for HTTP Mode |
channels.slack.dmPolicy | string | pairing | DM policy |
channels.slack.allowFrom | string[] | (none) | Allowed user IDs |
channels.slack.allowBots | boolean | false | Accept messages from bots |
channels.slack.historyLimit | number | 50 | Context message count |
channels.slack.replyToMode | string | off | off, first, or all |
channels.slack.streaming | string | off | off, partial, block, or progress |
channels.slack.nativeStreaming | boolean | false | Use Slack native streaming API when streaming=partial |
channels.slack.textChunkLimit | number | 4000 | Max chars per chunk |
channels.slack.mediaMaxMb | number | 20 | Max media size |
channels.slack.typingReaction | string | (none) | Emoji shortcode shown while reply is running |
channels.slack.reactionNotifications | string | own | off, own, all, or allowlist |
channels.slack.configWrites | boolean | true | Set false to block Slack-initiated config writes |
Thread behavior (channels.slack.thread.*):
| Key | Default | Description |
|---|---|---|
historyScope | thread | thread or channel |
inheritParent | false | Copy parent channel transcript to new threads |
Slash command (channels.slack.slashCommand.*):
| Key | Default | Description |
|---|---|---|
enabled | false | Enable slash command support |
name | openclaw | Slash command name |
sessionPrefix | slack:slash | Session key prefix |
ephemeral | true | Respond with ephemeral messages |
Google Chat config (channels.googlechat.*)
| Key | Description |
|---|---|
channels.googlechat.serviceAccountFile | Path to service account JSON file |
channels.googlechat.serviceAccount | Inline service account JSON |
channels.googlechat.serviceAccountRef | SecretRef for service account |
channels.googlechat.audienceType | app-url or project-number |
channels.googlechat.audience | Audience URL or project number |
channels.googlechat.webhookPath | Path for incoming webhooks |
channels.googlechat.botUser | Bot user identifier (users/<id>) |
channels.googlechat.dmPolicy | Via dm.policy key |
channels.googlechat.mediaMaxMb | Max media size |
channels.googlechat.typingIndicator | message or off |
Env fallbacks: GOOGLE_CHAT_SERVICE_ACCOUNT, GOOGLE_CHAT_SERVICE_ACCOUNT_FILE.
Signal config (channels.signal.*)
| Key | Description |
|---|---|
channels.signal.enabled | Enable Signal channel |
channels.signal.account | Optional phone number binding |
channels.signal.dmPolicy | DM access policy |
channels.signal.allowFrom | Allowed numbers or UUIDs (uuid:... format) |
channels.signal.configWrites | Allow/deny Signal-initiated config writes |
Mattermost config (channels.mattermost.*)
Mattermost requires the plugin: openclaw plugins install @openclaw/mattermost.
| Key | Default | Description |
|---|---|---|
channels.mattermost.botToken | (none) | Mattermost bot token |
channels.mattermost.baseUrl | (none) | Mattermost server URL |
channels.mattermost.dmPolicy | pairing | DM access policy |
channels.mattermost.chatmode | oncall | oncall (mention), onmessage (all), onchar (prefix trigger) |
channels.mattermost.oncharPrefixes | (none) | Trigger prefixes for onchar mode |
channels.mattermost.textChunkLimit | 4000 | Max chars per chunk |
channels.mattermost.requireMention | (none) | Require @mention before responding |
channels.mattermost.commands.native | false | Enable Mattermost native slash commands |
channels.mattermost.commands.callbackPath | (none) | Path for command callbacks |
channels.mattermost.commands.callbackUrl | (none) | Full URL if behind a reverse proxy |
Agent and model config options (agents.*)
Controls the agent runtime: workspace, models, thinking mode, heartbeat, and sandboxing.
agents.defaults.*
| Key | Type | Default | Description |
|---|---|---|---|
agents.defaults.workspace | string | (none) | Agent working directory |
agents.defaults.userTimezone | string | (none) | Timezone for agent date/time context |
agents.defaults.model.primary | string | (none) | Primary model (provider/model format) |
agents.defaults.model.fallbacks | string[] | (none) | Fallback models in order |
agents.defaults.models.<id>.* | object | (none) | Model catalog and allowlist for /model command |
agents.defaults.imageModel.primary | string | (none) | Default image generation model |
agents.defaults.imageMaxDimensionPx | number | 1200 | Max dimension for vision tool images. Lower values reduce token usage |
agents.defaults.thinkingDefault | string | off | off, low, or high |
agents.defaults.verboseDefault | string | off | Verbose mode default |
agents.defaults.elevatedDefault | string | off | Elevated tool permissions default |
agents.defaults.timeoutSeconds | number | 600 | Max session run time |
agents.defaults.mediaMaxMb | number | 5 | Max media file size for agent |
agents.defaults.maxConcurrent | number | 3 | Max concurrent agent sessions |
Heartbeat (agents.defaults.heartbeat.*)
| Key | Default | Description |
|---|---|---|
every | (none) | Interval string (30m, 2h). Set 0m to disable |
target | last | last, whatsapp, telegram, discord, or none |
directPolicy | allow | allow or block for DM-style heartbeat targets |
prompt | (none) | Custom heartbeat prompt |
ackMaxChars | (none) | Max chars for acknowledgment |
model | (none) | Model to use for heartbeat runs |
to | (none) | Override delivery target |
Sandbox (agents.defaults.sandbox.*)
| Key | Default | Description |
|---|---|---|
mode | off | off, non-main, or all |
scope | agent | session, agent, or shared |
workspaceRoot | (none) | Base dir for sandbox workspaces |
docker.image | (none) | Docker image name |
docker.workdir | /workspace | Working directory inside container |
docker.readOnlyRoot | (none) | Read-only root filesystem |
docker.network | none | Network mode |
docker.user | (none) | User inside container |
docker.tmpfs | (none) | Tmpfs mounts |
browser.enabled | false | Enable browser tools inside sandbox |
Build the sandbox image first: scripts/sandbox-setup.sh.
Memory search (agents.defaults.memorySearch.*)
| Key | Description |
|---|---|
provider | Search provider (e.g. gemini) |
model | Embedding model |
remote.apiKey | API key for remote search provider |
extraPaths | Additional directories to include in search |
Per-agent list (agents.list[])
Override defaults per agent. Each entry requires id. Common per-agent keys:
| Key | Description |
|---|---|
id | Agent identifier |
default | Set true to mark as the default agent |
model.primary | Override primary model |
thinkingDefault | Override thinking mode |
fastModeDefault | Enable fast mode for this agent |
reasoningDefault | Show reasoning output |
groupChat.mentionPatterns | Patterns that trigger this agent in groups |
Session management config (session.*)
Controls conversation continuity, scoping, and cleanup.
| Key | Type | Default | Description |
|---|---|---|---|
session.dmScope | string | main | main, per-peer, per-channel-peer, per-account-channel-peer |
session.reset.mode | string | (none) | daily, idle, or manual |
session.reset.atHour | number | 4 | Hour (0-23) for daily reset |
session.reset.idleMinutes | number | (none) | Minutes idle before reset |
session.resetByChannel.<provider>.* | object | (none) | Per-channel reset overrides |
session.resetTriggers | string[] | (none) | Chat commands that trigger a reset |
session.store | string | (none) | Custom path for sessions.json |
Thread bindings (session.threadBindings.*):
| Key | Description |
|---|---|
enabled | Enable thread-bound session features |
idleHours | Auto-unfocus idle hours |
maxAgeHours | Hard max age in hours |
Session maintenance (session.maintenance.*):
| Key | Default | Description |
|---|---|---|
mode | warn | warn, prune, or archive |
pruneAfter | 30d | Age at which sessions are pruned |
maxEntries | 500 | Max session entries |
rotateBytes | 10mb | Rotate sessions.json at this size |
resetArchiveRetention | 30d | How long to keep reset archives |
maxDiskBytes | (none) | Disk quota for session storage |
Send policy (session.sendPolicy.*):
| Key | Description |
|---|---|
default | allow or deny |
rules[] | Array of {action, match} rules |
Cron job config options (cron.*)
Controls scheduled task execution behavior.
| Key | Type | Default | Description |
|---|---|---|---|
cron.enabled | boolean | false | Enable the cron runner |
cron.maxConcurrentRuns | number | 2 | Max jobs running at the same time |
cron.sessionRetention | string/boolean | 24h | How long to keep completed isolated run sessions. Set false to disable cleanup |
cron.runLog.maxBytes | string | 2mb | Max size for per-job run logs |
cron.runLog.keepLines | number | 2000 | Lines to retain in run logs |
Cron jobs are defined separately in ~/.openclaw/cron/jobs.json. The cron.* config keys only control the runner behavior.
Webhooks and hooks config (hooks.*)
Enables HTTP webhook ingestion on the gateway.
| Key | Type | Default | Description |
|---|---|---|---|
hooks.enabled | boolean | false | Enable webhook endpoint |
hooks.token | string | (none) | Shared secret for webhook verification |
hooks.path | string | /hooks | URL path for the webhook endpoint |
hooks.defaultSessionKey | string | (none) | Default session key for inbound hooks |
hooks.allowRequestSessionKey | boolean | false | Allow callers to specify session key in request |
hooks.allowedSessionKeyPrefixes | string[] | (none) | Allowed session key prefixes |
hooks.mappings[] | array | (none) | Routing rules: match.path, action, agentId, deliver |
Treat all webhook payload content as untrusted input. Keep allowUnsafeExternalContent flags disabled except for tightly scoped debugging.
Tool permissions config (tools.*)
Controls which tools the agent can use, execution limits, and media handling.
| Key | Type | Description |
|---|---|---|
tools.allow | string[] | Allowlist of tool names |
tools.deny | string[] | Denylist of tool names |
Exec settings (tools.exec.*):
| Key | Default | Description |
|---|---|---|
backgroundMs | 10000 | Milliseconds before backgrounding a command |
timeoutSec | 1800 | Max exec run time in seconds |
cleanupMs | 1800000 | Time before cleaning up background processes |
Elevated permissions (tools.elevated.*):
| Key | Description |
|---|---|
enabled | Enable elevated tool permissions |
allowFrom.<channel>[] | Senders allowed to use elevated tools per channel |
Media tools (tools.media.audio., tools.media.video.):
| Key | Default | Description |
|---|---|---|
audio.enabled | false | Enable audio transcription |
audio.maxBytes | 20971520 | Max audio file size (~20MB) |
audio.models[] | (none) | Transcription models to try |
audio.timeoutSeconds | 120 | Transcription timeout |
video.enabled | false | Enable video analysis |
video.maxBytes | 52428800 | Max video file size (~50MB) |
video.models[] | (none) | Vision models for video |
Identity, logging, routing, and environment config
Identity (identity.*)
| Key | Description |
|---|---|
identity.name | Agent display name |
identity.theme | Short persona description |
identity.emoji | Agent emoji |
Logging (logging.*)
| Key | Default | Description |
|---|---|---|
logging.level | info | Log level: debug, info, warn, error |
logging.file | (none) | Path to log file |
logging.consoleLevel | info | Console log level |
logging.consoleStyle | pretty | pretty or json |
logging.redactSensitive | (none) | tools to redact sensitive tool output |
Messages (messages.*)
| Key | Description |
|---|---|
messages.messagePrefix | Prefix prepended to messages |
messages.responsePrefix | Prefix for agent responses |
messages.ackReaction | Emoji reaction for acknowledgment |
messages.ackReactionScope | When to react: all, group-mentions, etc. |
Routing (routing.*)
| Key | Default | Description |
|---|---|---|
routing.groupChat.mentionPatterns | (none) | Text patterns that trigger the agent in groups |
routing.groupChat.historyLimit | (none) | Context message count for group chats |
routing.queue.mode | collect | Queue mode: collect or immediate |
routing.queue.debounceMs | 1000 | Debounce delay before processing collected messages |
routing.queue.cap | 20 | Max messages to collect |
routing.queue.drop | summarize | What to do when queue overflows |
Environment injection (env.*)
| Key | Description |
|---|---|
env.vars.* | Key-value pairs injected as environment variables |
env.shellEnv.enabled | Inherit the shell environment when starting |
env.shellEnv.timeoutMs | Timeout for shell env resolution |
env.<PROVIDER_KEY> | Direct API key injection (e.g. env.OPENROUTER_API_KEY) |
Auth profiles (auth.*)
Auth profile metadata lives in openclaw.json. Secrets live in ~/.openclaw/auth-profiles.json.
| Key | Description |
|---|---|
auth.profiles.<id>.provider | Provider name (anthropic, openai, etc.) |
auth.profiles.<id>.mode | api_key or oauth |
auth.order.<provider>[] | Priority order of profile IDs for that provider |
Custom model providers (models.*)
| Key | Description |
|---|---|
models.mode | merge (add to built-ins) or replace (use only custom) |
models.providers.<id>.baseUrl | Custom provider base URL |
models.providers.<id>.apiKey | API key for custom provider |
models.providers.<id>.api | API type: openai-responses, etc. |
models.providers.<id>.models[] | Model definitions for this provider |
Key terms
JSON5: A superset of JSON that allows comments (//, /* */) and trailing commas. OpenClaw reads openclaw.json as JSON5.
dmPolicy: A per-channel setting controlling who can DM the bot. Options: pairing, allowlist, open, disabled.
Sandbox: An isolated Docker container for agent tool execution. Controlled by agents.defaults.sandbox.*.
SecretRef: A reference to a secret stored outside the config file. Supports env, file, and exec sources.
Socket Mode vs HTTP Mode (Slack): Socket Mode requires both botToken and appToken. HTTP Mode requires botToken and signingSecret.
FAQ
What happens in OpenClaw if I add an unknown key to openclaw.json?
OpenClaw validates the config against a strict schema on every startup. If you add a key that does not exist in the schema, or use the wrong type, the gateway refuses to start. You will see an error in openclaw doctor output. Use openclaw doctor --fix to apply automatic repairs for common issues. The only key that bypasses strict validation is the root-level $schema string.
Where does OpenClaw look for the ANTHROPIC_API_KEY and what is the priority order when multiple keys exist?
OpenClaw checks these sources in descending priority: OPENCLAW_LIVE_ANTHROPIC_KEY (single-session override), then ANTHROPIC_API_KEYS (comma-separated rotation list), then ANTHROPIC_API_KEY (standard single key), then any ANTHROPIC_API_KEY_* wildcard matches. For daemon setups, place the key in ~/.openclaw/.env. Keys rotate automatically on rate-limit errors (429, quota exceeded). Non-rate-limit errors do not trigger rotation.
How do I set channels.telegram.botToken in OpenClaw without putting the token directly in openclaw.json?
Set the TELEGRAM_BOT_TOKEN environment variable instead. OpenClaw reads it automatically as the fallback for the default Telegram account. For daemon setups, add it to ~/.openclaw/.env. You can also use channels.telegram.tokenFile to point to a regular file containing the token. This keeps secrets out of the JSON config while remaining file-based. Symlinks are rejected for security.
What is the difference between dmPolicy pairing and allowlist in OpenClaw channel config?
Both restrict who can DM the bot, but with different mechanics. pairing (the default) means any unknown sender gets a one-time code to request access. The owner reviews and approves or ignores it. Codes expire after 1 hour, with a cap of 3 pending requests per channel. allowlist is stricter: only senders in allowFrom or the paired allow store can contact the bot. Unknown senders are silently dropped with no code issued.
Can I run openclaw.json changes without restarting the gateway?
Yes. The OpenClaw gateway watches ~/.openclaw/openclaw.json for changes and applies them automatically via hot reload. You do not need to restart the gateway after editing the config file. The CLI method (openclaw config set <key> <value>) also applies changes immediately. If the change causes a schema validation failure, the gateway logs an error and continues running on the previous valid config.
Related resources
- Build a Website with OpenClaw: Practical OpenClaw project walkthrough
- How to Fix OpenClaw Discord Bot Not Responding: Discord channel config troubleshooting
- Best VPS for OpenClaw: Hosting recommendations and gateway setup
Changelog
| Date | Change |
|---|---|
| 2026-03-23 | Initial publication |
Fixes when it breaks. Workflows when it doesn't.
OpenClaw guides, configs, and troubleshooting notes. Every two weeks.



