Last updated: 2026-05-16

OpenClaw Configuration Reference

OpenClaw stores all configuration in ~/.openclaw/openclaw.json using JSON5 format (comments allowed, trailing commas OK). The gateway validates strictly on startup — unknown keys or wrong types prevent it from starting. This is the complete reference for every top-level object.

Quick commands

openclaw onboard — interactive first-time setup wizard
openclaw config get agents.defaults.model — read a specific key
openclaw config set agents.defaults.heartbeat.every "2h" — set a value
openclaw config schema — view full JSON Schema
openclaw doctor — diagnose config problems
openclaw doctor --fix — auto-repair common issues
openclaw goal set "..." — set a persistent agent goal
openclaw goal list — list active goals
openclaw goal clear — remove all goals

Top-Level Structure

The config file is a single JSON5 object. All top-level keys are optional — OpenClaw applies defaults for anything missing.

KeyPurpose
agentsAgent defaults, model list, skills, sandbox settings, heartbeat
channelsChannel integrations: WhatsApp, Telegram, Discord, Slack, email, etc.
sessionConversation scope, thread bindings, daily reset behaviour
gatewayServer port, auth token, health monitoring, hot-reload mode
cronScheduled job settings, concurrency, session retention, run logs
hooksWebhook endpoints, routing mappings, security tokens
envEnvironment variables, secrets, shell imports
uiWeb UI customisation
broadcastMulti-client configuration

agents — Model, Skills & Sandbox

{
  agents: {
    defaults: {
      workspace: "~/.openclaw/workspace",

      // Primary model + fallbacks
      model: {
        primary: "anthropic/claude-sonnet-4-6",
        fallbacks: ["openai/gpt-4.1"]
      },

      // Model allowlist — defines which models users can switch to
      models: {
        "anthropic/claude-sonnet-4-6": { alias: "Sonnet" },
        "anthropic/claude-haiku-4-5":  { alias: "Haiku"  },
        "openai/gpt-4.1":              { alias: "GPT4"   }
      },

      // Skills enabled by default for all agents
      skills: ["github", "weather", "daily-brief"],

      // Sandbox controls which tools run in isolation
      sandbox: {
        mode: "non-main",  // off | non-main | all
        scope: "agent"     // session | agent | shared
      },

      // Heartbeat: proactive check-ins on a schedule
      heartbeat: {
        every: "30m",   // cron or duration string. "0" = disabled
        target: "last"  // "last" = most recent session
      },

      // Worktree isolation (added v2.1.143)
      worktree: {
        baseRef:     "head",  // fresh | head — branch point for new worktrees
        bgIsolation: true     // true = background worktrees run in isolated environment
      }
    },

    // Multiple named agents
    list: [
      {
        id: "main",
        default: true,
        workspace: "~/.openclaw/workspace",
        skills: ["github", "daily-brief"],
        groupChat: {
          mentionPatterns: ["@openclaw", "openclaw"]
        }
      },
      {
        id: "work",
        workspace: "~/.openclaw/workspace-work",
        skills: ["github", "jira"]
      }
    ]
  }
}

Sandbox Modes

ModeBehaviour
offNo sandboxing — all skills run with full host access
non-mainNon-primary agents run sandboxed; main agent runs direct (recommended)
allAll agents sandboxed — most secure, slowest startup

channels — All Integrations

Every channel uses the same DM access pattern. The key config fields are consistent across all providers:

{
  channels: {
    <provider>: {
      enabled: true,
      dmPolicy:    "pairing",    // pairing | allowlist | open | disabled
      allowFrom:   ["+15555550123"],  // phone numbers, user IDs, or "*"
      groupPolicy: "mention",    // open | allowlist | disabled
      groups: {
        "*": { requireMention: true }
      }
    }
  }
}

dmPolicy Values

ValueBehaviourUse case
pairingNew users send /start, get a code, you approve it on the serverPersonal use — most secure default
allowlistOnly user IDs in allowFrom can DM the agentFamily/team where you know all IDs upfront
openAnyone who discovers the bot can message itPublic bots only — not recommended for personal agents
disabledDMs completely blocked; group-only accessGroup-only deployments

Telegram

{
  channels: {
    telegram: {
      enabled: true,
      botToken: "${TELEGRAM_BOT_TOKEN}",
      dmPolicy: "pairing",
      allowFrom: ["8734062810"],   // your numeric Telegram user ID
      groupPolicy: "allowlist",
      groups: {
        "-1001234567890": {        // group chat ID (negative number)
          requireMention: true,
          allowFrom: ["8734062810", "745123456"]
        }
      }
    }
  }
}

WhatsApp

{
  channels: {
    whatsapp: {
      enabled: true,
      dmPolicy: "allowlist",
      allowFrom: ["+15555550123"],   // E.164 format
      groupPolicy: "mention"
    }
  }
}

Discord

{
  channels: {
    discord: {
      enabled: true,
      botToken: "${DISCORD_BOT_TOKEN}",
      applicationId: "123456789012345678",
      dmPolicy: "allowlist",
      allowFrom: ["your-discord-user-id"]
    }
  }
}

session — Scope & Reset

{
  session: {
    // How conversation history is scoped
    dmScope: "per-channel-peer",
    // Options:
    //   main                — one global session for all DMs
    //   per-peer            — one session per sender (across channels)
    //   per-channel-peer    — one session per sender per channel (recommended)
    //   per-account-channel-peer — adds account-level isolation

    threadBindings: {
      enabled:    true,
      idleHours:  24,   // thread expires after 24h of inactivity
      maxAgeHours: 0    // 0 = no hard limit
    },

    reset: {
      mode:        "daily",  // daily | idle | off
      atHour:      4,        // 4 AM local time
      idleMinutes: 120       // reset after 2h of no messages
    }
  }
}

gateway — Server Settings

{
  gateway: {
    port: 18789,
    bind: "127.0.0.1",   // NEVER change to 0.0.0.0 on a public VPS

    auth: {
      token: "${OPENCLAW_GATEWAY_TOKEN}"
    },

    reload: {
      mode:       "hybrid",  // hybrid | hot | restart | off
      debounceMs: 300
    },

    // Health monitoring
    channelHealthCheckMinutes:          5,
    channelStaleEventThresholdMinutes: 30,
    channelMaxRestartsPerHour:         10
  }
}

Reload Modes

ModeBehaviour
hybridMost changes apply live; gateway changes queue for next restart (recommended)
hotAll changes apply immediately — some instability possible
restartFull restart on any config change
offManual restart required for all changes

cron — Scheduled Jobs

The cron block controls the scheduler's global behaviour. Individual jobs are defined inside the agent's workspace HEARTBEAT.md file (see SOUL.md & Agent Personas).

{
  cron: {
    enabled:           true,
    maxConcurrentRuns: 2,        // max simultaneous job runs
    sessionRetention:  "24h",    // how long cron session logs are kept

    runLog: {
      maxBytes:  "2mb",
      keepLines: 2000
    }
  }
}

Individual cron jobs are scheduled inside your agent's workspace. Typical example in HEARTBEAT.md:

# HEARTBEAT TASKS

## Daily Morning Brief — 7:00 AM
Schedule: 0 7 * * *
Action: Run the daily-brief skill and send result to Telegram

## Disk Check — Every 30 Minutes
Schedule: */30 * * * *
Action: Check disk usage. If any partition > 85%, alert immediately.

## Weekly Security Audit — Monday 9 AM
Schedule: 0 9 * * 1
Action: Run healthcheck skill and summarise results to my DM.

event hooks — Behaviour on Block

Event hooks let you configure how agents react when a skill or tool call is denied by a policy or permission rule. Added in v2.1.139.

{
  agents: {
    defaults: {
      hooks: {
        // What the agent does when a skill/tool call is blocked
        continueOnBlock: false,
        // false   — agent stops and reports the block to the user (default, safest)
        // true    — agent skips the blocked action and continues to the next step
        // "ask"   — agent pauses and asks the user whether to proceed

        // What the agent does when given a goal via `openclaw goal set`
        onGoalSet: "acknowledge",
        // acknowledge — agent confirms the goal was received
        // silent      — no acknowledgement, goal activates immediately
      }
    }
  }
}

continueOnBlock Values

ValueBehaviourUse case
falseAgent halts and notifies the user of the blocked actionDefault — safest for personal agents handling sensitive data
trueAgent skips the blocked step and continues the taskAutomated pipelines where partial completion is acceptable
"ask"Agent pauses and asks the user before proceedingInteractive sessions where you want manual oversight
Keep continueOnBlock: false for personal agents

Setting continueOnBlock: true means a blocked file-write or API call will be silently skipped. This is useful for automation but can produce incomplete results without any warning. Leave it false unless you have a specific reason to change it.

env — Secrets & Environment Variables

{
  env: {
    // Direct values (less secure — prefer shellEnv below)
    OPENROUTER_API_KEY: "sk-or-...",

    // Nested vars object — same behaviour
    vars: {
      GROQ_API_KEY: "gsk-..."
    },

    // Import from shell environment (most secure)
    shellEnv: {
      enabled:   true,
      timeoutMs: 15000
    }
  }
}

Reference env vars anywhere in the config with "${VAR_NAME}". Only uppercase names are supported. Missing variables cause a startup error — use openclaw doctor to diagnose.

Keep secrets out of the config file

The best practice is to use shellEnv: { enabled: true } and export your API keys in your shell profile (~/.zshrc or ~/.bashrc). This way the config file itself contains no secrets and can be safely version-controlled.

Multi-Agent Routing

Route different channels or accounts to different agents using bindings:

{
  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"      } },
    { agentId: "work", match: { channel: "telegram"                         } }
  ]
}

Config Includes — Split Into Multiple Files

Large configs can be split across files using $include:

// ~/.openclaw/openclaw.json
{
  agents:    { $include: "./agents.json5"                              },
  channels:  { $include: "./channels.json5"                            },
  broadcast: { $include: ["./clients/a.json5", "./clients/b.json5"]   }
}

Single files replace the object they're assigned to. Arrays deep-merge in order. This lets you keep Telegram credentials in a separate file with tighter filesystem permissions.

← Back to OpenClaw hub · See also: Telegram Setup · Security Hardening · Cost Optimisation

📬 Weekly Digest — In Your Inbox

One email a week: top news, releases, and our deepest new guide. No spam. Same content via RSS if you prefer.