← Back to dashboard

OpenClaw Integration Guide

What is OpenClaw?

The real model

A daemon plus a CLI, not an SDK — and the config file is the whole programming model.

There is nothing to import: `npm i -g openclaw@latest --allow-scripts openclaw` (npm 12+ blocks the lifecycle script without that flag, which is the #1 "installed but broken" report) then `openclaw onboard --install-daemon`. Agents live in `agents.entries.<id>`; once you set `agents.ownership: "explicit"` there is no default agent, so an unbound channel routes nowhere. Tool policy is enforced *before* the model call — a denied tool's schema never reaches the model — which makes `tools.deny: ["exec","terminal","process","code_execution"]` a real capability removal rather than a polite request, and it is mandatory on anything a stranger can DM. Skills are `SKILL.md` files resolved across six precedence tiers where the frontmatter `name` beats the folder name, so shadowed skills fail silently. `gateway.bind` defaults to loopback on a host but flips to `auto` (0.0.0.0) inside a container — auth is refused-if-missing on non-loopback, which is the guardrail. For codeAmani the fit is sharp and bounded: it is the fastest way to put a Claude-backed brain behind a WhatsApp number for internal ops and for prototyping the Kenya builds, but the WhatsApp channel is QR pairing against a WhatsApp account, not the Meta Business Cloud API — customer-facing volume still ships on the official API with templates and the 24-hour window.

Six OpenClaw primitives

One daemon, one JSON5 config, one CLI — channels in, agents in the middle, tools on a leash.

Text
 ██████╗ ██████╗ ███████╗███╗   ██╗ ██████╗██╗      █████╗ ██╗    ██╗
██╔═══██╗██╔══██╗██╔════╝████╗  ██║██╔════╝██║     ██╔══██╗██║    ██║
██║   ██║██████╔╝█████╗  ██╔██╗ ██║██║     ██║     ███████║██║ █╗ ██║
██║   ██║██╔═══╝ ██╔══╝  ██║╚██╗██║██║     ██║     ██╔══██║██║███╗██║
╚██████╔╝██║     ███████╗██║ ╚████║╚██████╗███████╗██║  ██║╚███╔███╔╝
 ╚═════╝ ╚═╝     ╚══════╝╚═╝  ╚═══╝ ╚═════╝╚══════╝╚═╝  ╚═╝ ╚══╝╚══╝

OpenClaw Integration Guide

Focus: Running a self-hosted OpenClaw Gateway as a personal/ops AI agent that lives inside WhatsApp, Telegram, Slack and friends — install, config, agents, skills, MCP, and how to harden it before it leaves loopback.

Overview

OpenClaw (MIT, github.com/openclaw/openclaw, developed in the open by the non-profit OpenClaw Foundation — openclaw.org) is "a personal AI assistant that runs on your devices and meets you in the channels you already use." It is not a library you import — there is no import { OpenClaw }. It is a daemon plus a CLI: you install the openclaw npm package globally, run an onboarding wizard, and end up with a long-lived Gateway process on 127.0.0.1:18789 that owns your messaging connections, your model providers, your agent sessions, and your tool policy.

The design point that makes it different from an agent framework: the messaging platform is the UI. There is no app to build. You DM a WhatsApp or Telegram number, the Gateway routes that message to an agent, the agent runs tools in a workspace directory on your machine, and the reply comes back down the same channel. A browser Control UI (openclaw dashboard) exists for administration, not as the primary surface.

OpenClawAgent SDK / frameworkManaged bot platform
ShapeDaemon + CLI you self-hostLibrary you compile into an appHosted SaaS
UIExisting messaging appsYou build itVendor's console
ModelBring your own key, 50+ providersWhatever you wireVendor's models
SecretsYour disk (~/.openclaw/)Your envVendor holds them
Ops burdenYours (process, updates, auth)Yours (deploy)None
Multi-tenantNo — single-operator by designYes, if you build itYes

Official Documentation


Install

Requirements: Node 22.22.3+, 24.15+, or 25.9+ (Node 26 recommended), plus an API key from at least one model provider.

The one-liner installers handle Node and the daemon for you:

Bash
# macOS / Linux / WSL2
curl -fsSL --proto '=https' --tlsv1.2 https://openclaw.ai/install.sh | bash
PowerShell
# Windows (PowerShell)
iwr -useb https://openclaw.ai/install.ps1 | iex

If you manage Node yourself, install the npm package globally. npm 12+ blocks unapproved lifecycle scripts, so the --allow-scripts flag is required (this is the exact form the docs ship):

Bash
npm install -g openclaw@latest --allow-scripts openclaw
openclaw onboard --install-daemon
Bash
# pnpm
pnpm add -g --allow-build=openclaw openclaw@latest && openclaw onboard --install-daemon

# bun
bun add -g --trust openclaw@latest && openclaw onboard --install-daemon

Verify:

Bash
openclaw --version
openclaw doctor           # config + environment diagnostics
openclaw gateway status   # should report listening on 18789
openclaw dashboard        # opens the Control UI in a browser

Releases are date-versioned (2026.7.1-2 style) and ship on four dist-tags: latest, extended-stable, beta, alpha. Pin extended-stable on anything you don't want moving under you.


Configuration

State lives in $HOME/.openclaw/; the config file is JSON5 (comments and trailing commas allowed) and is normally ~/.openclaw/openclaw.json. Override the path with OPENCLAW_CONFIG_PATH (keep it a real file — OpenClaw rewrites config atomically, so a symlinked openclaw.json gets its target replaced). You can edit it directly: the Gateway watches the file and hot-reloads changes, and it refuses to start on a config that fails validation. Still, prefer the CLI so a typo is caught up front rather than at reload:

Bash
openclaw config file            # print the resolved config path
openclaw config get agents.defaults.model
openclaw config set agents.defaults.model.primary "anthropic/claude-opus-5"
openclaw config validate
openclaw config schema          # full JSON Schema

Model providers

Models are addressed as "provider/model". Provider credentials live under env.vars in the config (or in the process environment):

JSON5
{
  env: {
    vars: {
      ANTHROPIC_API_KEY: "sk-ant-...", // resolved from your secret manager, not committed
    },
  },
  agents: {
    defaults: {
      model: {
        primary: "anthropic/claude-opus-5",
        fallbacks: ["anthropic/claude-sonnet-5"],
      },
      utilityModel: "anthropic/claude-fable-5", // cheap model for routing/summarisation
      thinkingDefault: "low",
    },
  },
}
Bash
openclaw onboard --anthropic-api-key "$ANTHROPIC_API_KEY"
openclaw models list --provider anthropic

The "provider/model" shape and the Claude ids used here are verified against docs/providers/anthropic.md in the tree, which documents anthropic/claude-opus-5, anthropic/claude-sonnet-5, anthropic/claude-fable-5, anthropic/claude-mythos-5, and dated builds such as anthropic/claude-opus-4-8. (Anthropic can also be driven through an existing Claude Code CLI login on the same host instead of an API key; for a long-lived Gateway, prefer a dedicated ANTHROPIC_API_KEY.)

OpenClaw's provider directory covers 50+ backends — Anthropic, OpenAI, Google, Mistral, Cohere, Bedrock, Groq, Together AI, DeepSeek, Perplexity, plus local runtimes (Ollama, LM Studio, vLLM, llama.cpp, SGLang). Per codeAmani's AI routing policy, keep primary on Anthropic Claude and reserve cheaper tiers for utilityModel.

Agents

Agents are config objects, not code. agents.defaults sets the inherited baseline; agents.entries.<id> overrides per agent; bindings route channels to agents.

JSON5
{
  agents: {
    ownership: "explicit", // required once you run more than one agent
    defaults: {
      workspace: "~/.openclaw/workspace",
      model: { primary: "anthropic/claude-opus-5" },
      skills: ["duka-inventory"],
      heartbeat: { every: "30m" },
    },
    entries: {
      ops: {
        name: "Ops Agent",
        workspace: "~/.openclaw/workspace-ops",
        model: "anthropic/claude-opus-5",
        identity: { name: "Amani", emoji: "🕊️" },
      },
      support: {
        workspace: "~/.openclaw/workspace-support",
        model: { primary: "anthropic/claude-sonnet-5", fallbacks: [] },
        skills: ["docs-search"],
      },
    },
  },
  bindings: [
    { agentId: "ops", match: { channel: "whatsapp", accountId: "internal" } },
    { agentId: "support", match: { channel: "telegram" } },
  ],
}

Binding resolution is deterministic and narrows outward: peer → guild/team → account → channel-wide fallback. With ownership: "explicit" there is no default agent — an unbound channel simply has nowhere to route.

Bash
openclaw agents list
openclaw agents add
openclaw agents bind          # attach an agent to a channel/account

Channels

Telegram, Reef, and WebChat are bundled. Everything else — WhatsApp, Slack, Discord, Signal, iMessage, Matrix, Microsoft Teams, Google Chat, LINE, SMS, IRC, Twitch and more — is an official plugin installed on demand.

Bash
openclaw plugins install clawhub:@openclaw/whatsapp
openclaw channels add --channel whatsapp     # interactive; installs the plugin if missing
openclaw channels login --channel whatsapp   # QR pairing
openclaw channels list

WhatsApp

Set the access policy before you log in, or the first stranger to message the number becomes a conversation:

JSON5
{
  channels: {
    whatsapp: {
      dmPolicy: "pairing",              // unknown senders need owner approval
      allowFrom: ["+254712345678"],
      groupPolicy: "allowlist",
      groupAllowFrom: ["+254712345678"],
      textChunkLimit: 4000,             // default; lower it for 2G/3G users
      sendReadReceipts: true,
      replyToMode: "first",             // off | first | all | batched
    },
  },
}
dmPolicyEffect
pairingUnknown senders raise an approval request (expires after 1h, max 3 pending)
allowlistOnly numbers in allowFrom get through
openEveryone — requires an explicit allowFrom: ["*"]
disabledNo DMs at all

Approve a pending pairing from the Control UI (Settings → Channels → DM access requests) or the CLI:

Bash
openclaw pairing approve whatsapp <CODE>

Session credentials land in ~/.openclaw/credentials/whatsapp/<accountId>/creds.json — treat that directory as a secret. Multi-account setups live under channels.whatsapp.accounts.<id>, which is how one Gateway serves a personal and a business number with different agents bound to each.

Read this before shipping (settled against the source): the WhatsApp channel links a WhatsApp account via QR pairing over WhatsApp Web, not the Meta WhatsApp Business Cloud API and not Twilio. This is not inference — extensions/whatsapp depends on baileys (the WhatsApp-Web multi-device library), its login path is QR-only (login-qr-*, onQr), and the docs state it plainly: "production-ready via WhatsApp Web (Baileys). The gateway owns the linked session(s); there is no separate Twilio WhatsApp channel." Perfect for an internal ops number or a prototype; the wrong tool for customer-facing volume on a business number — see codeAmani notes.


Skills

Skills are the extension unit for agent behaviour, and they are just files: a directory containing a SKILL.md with YAML frontmatter plus a markdown body, following the AgentSkills spec.

Markdown
---
name: mpesa-reconcile
description: Reconcile M-Pesa C2B callbacks against open orders and flag mismatches.
---

# M-Pesa reconciliation

When asked to reconcile payments:
1. Read `orders.csv` from the workspace.
2. Match on `CheckoutRequestID`, never on amount alone.
3. Report unmatched rows as a table; never auto-refund.

Discovery walks up to 6 levels deep inside configured roots, and the name in frontmatter wins over the directory name. When the same name appears twice, the highest-priority source wins:

PrioritySource
1Workspace skills — <workspace>/skills
2Project agent skills — <workspace>/.agents/skills
3Personal agent skills — ~/.agents/skills
4Managed/local skills — <state-dir>/skills
5Bundled skills (shipped with OpenClaw)
6Extra directories + plugin skills
Bash
openclaw skills install @owner/slug              # from ClawHub
openclaw skills install git:owner/repo@ref       # from a git ref
openclaw skills install ./path/to/skill --as mpesa-reconcile
openclaw skills install @owner/slug --global     # visible to every agent
openclaw skills verify @owner/slug               # trust check before install
openclaw skills update --all

At run time OpenClaw resolves eligible skills against gating rules and allowlists, injects skills.entries.<name>.env variables, and compiles a snapshot into the system prompt as XML. Skills surface as slash commands (/mpesa-reconcile) and as $mpesa-reconcile references inside a prompt; set disable-model-invocation: true in frontmatter to make a skill user-only.


Tools and tool policy

Agents get a broad built-in toolset — exec, process, terminal, code_execution; read/write/edit/apply_patch; web_search, x_search, web_fetch, browser; view_image, image_generate, tts; sessions_*, subagents, agents_wait, goal; cron and heartbeat_respond for background work; ask_user, message, screen.

Policy is enforced before the model call — a denied tool's schema is never sent for that turn, so the model cannot even attempt it:

JSON5
{
  agents: {
    entries: {
      support: {
        tools: {
          deny: ["exec", "terminal", "process", "code_execution"],
        },
      },
    },
  },
}

That deny-list is the single most important config line for any agent reachable from a public channel.

MCP servers

MCP servers plug in as first-class tool sources under mcp.servers, over stdio, streamable HTTP, or SSE:

Bash
openclaw mcp add
openclaw mcp login <name>            # OAuth for protected servers
openclaw mcp doctor <name> --probe   # reachability + capability probe
openclaw mcp status --verbose

Their tools flow through the same tool-profile and policy controls, and can be narrowed with toolFilter.include / toolFilter.exclude.


Gateway operations & security

One Gateway per host, one multiplexed port for WebSocket control, HTTP APIs, and the Control UI (the node/device bridge listens separately on 18790, and channel plugins that need an inbound webhook — e.g. MS Teams on 3978 — open their own).

Bash
openclaw gateway start
openclaw gateway status
openclaw gateway restart
openclaw logs
openclaw health

Port resolves --port → OPENCLAW_GATEWAY_PORT → gateway.port → 18789. Bind resolves CLI override → gateway.bind → loopback (containers default to auto, i.e. 0.0.0.0, unless Tailscale serve/funnel is active, which forces loopback).

Authentication is mandatory by default, and OpenClaw refuses to bind a non-loopback interface without it:

JSON5
{
  gateway: {
    bind: "loopback",
    auth: { token: "..." },            // or auth.password
    controlUi: {
      allowedOrigins: ["https://claw.internal.example"], // required for remote browsers
    },
  },
}

Equivalent env vars: OPENCLAW_GATEWAY_TOKEN, OPENCLAW_GATEWAY_PASSWORD. For a reverse proxy that terminates auth itself, set gateway.auth.mode: "trusted-proxy".

For remote access the docs push Tailscale/VPN first, SSH tunnel second — and an SSH tunnel does not bypass gateway auth; clients still send the token:

Bash
ssh -N -L 18789:127.0.0.1:18789 user@gateway-host

Control-plane clients (CLI, Control UI, macOS app, automation) and nodes (macOS/iOS/Android/headless devices exposing camera.*, screen.record, location.get) all speak the same typed WebSocket protocol: a mandatory connect handshake, then {type:"req", id, method, params} → {type:"res", id, ok, payload|error} with events pushed as {type:"event", event, payload, seq?, stateVersion?}. Side-effecting methods (send, agent) accept idempotency keys — use them, because a reconnect that replays a send is a duplicate WhatsApp message to a customer.


Environment Variables

Bash
# Model provider (codeAmani primary — see AI routing policy)
ANTHROPIC_API_KEY=sk-ant-...

# Gateway auth — required for any non-loopback bind
OPENCLAW_GATEWAY_TOKEN=...
# or
OPENCLAW_GATEWAY_PASSWORD=...

# Optional
OPENCLAW_GATEWAY_PORT=18789
OPENCLAW_CONFIG_PATH=/etc/openclaw/openclaw.json
OPENCLAW_SERVICE_REPAIR_POLICY=...   # hand lifecycle to an external supervisor

Repository layout (grounding)

If you clone openclaw/openclaw to read the source, the monorepo is pnpm-workspace'd (packages: [., ui, packages/*, extensions/*, examples/*]) and the names in the docs don't map 1:1 to folders:

In the treeIsNote
extensions/ (~157 pkgs)Channels + model providers + toolsWhat the docs and CLI call "plugins." Per the repo's own AGENTS.md: "Product/docs/UI/changelog wording: 'plugin/plugins'; extensions/ is internal." WhatsApp is extensions/whatsapp (pkg @openclaw/whatsapp), Anthropic is extensions/anthropic.
apps/Companion clientsandroid, ios, macos, linux, shared — the "nodes" that expose camera.*/screen.*/location.*.
config/Repo tooling configlint/tsconfig/budgets — not runtime Gateway config (that lives at ~/.openclaw/openclaw.json).
deploy/Deploy fragmentsjust fly.private.toml; root also ships Dockerfile, docker-compose.yml, fly.toml, render.yaml.
docs/The published docsSame paths as docs.openclaw.ai (e.g. docs/channels/whatsapp.md → /channels/whatsapp).
AGENTS.mdContributor policyCLAUDE.md is a symlink to it — edit AGENTS.md only.

The shipped docker-compose.yml starts the Gateway with --bind ${OPENCLAW_GATEWAY_BIND:-lan} and publishes 18789 (Gateway), 18790 (node/device bridge) and 3978 (MS Teams); it pins OPENCLAW_STATE_DIR/OPENCLAW_CONFIG_PATH/OPENCLAW_WORKSPACE_DIR under /home/node/.openclaw and expects OPENCLAW_GATEWAY_TOKEN — a containerised deploy is non-loopback by construction, so the token is mandatory.


codeAmani notes

  • Secrets stay on the host. Provider keys land in ~/.openclaw/openclaw.json under env.vars, and channel credentials in ~/.openclaw/credentials/. That whole directory is a secret store — never sync it, never bake it into an image, and pull the values from Hazina at provisioning time rather than committing them. Run gitleaks before any repo that references an OpenClaw config goes near a push.
  • Bind loopback, tunnel for the rest. The default loopback bind is the correct production posture. If you must reach it remotely, use Tailscale or the SSH tunnel and set gateway.auth.token — non-loopback without auth is refused, and that refusal is a feature. Also note the container default flips to auto (0.0.0.0), so a naive docker run is the one config that quietly exposes the port.
  • Deny exec on anything customer-facing. An OpenClaw agent can run shell commands in its workspace by default. For a public WhatsApp number, tools.deny: ["exec", "terminal", "process", "code_execution"] plus dmPolicy: "allowlist" is the baseline, and even then treat inbound messages as untrusted input — the same prompt-injection boundary as any webhook payload.
  • AI routing. Set model.primary to anthropic/claude-opus-5 per the house policy, keep a Sonnet fallback for provider blips, and point utilityModel at a cheap tier so routing and summarisation don't bill at flagship rates. OpenClaw's provider list also carries Together AI and DeepSeek if a project has already picked those.
  • Kenya-targeted projects — the honest fit. OpenClaw's messaging-first model maps cleanly onto the WhatsApp + M-Pesa builds (duka-order-bot, boda-dispatch, clinic-salon-booking): a self-hosted agent brain behind a WhatsApp number, with M-Pesa reconciliation logic expressed as a SKILL.md rather than app code. But the WhatsApp channel pairs by QR against a WhatsApp account, not the Meta WhatsApp Business Cloud API — so it is the right tool for the internal ops number, the merchant-side assistant, and fast prototyping of conversation flows, and the wrong one for customer-facing volume where a Cloud API number, template messages, and the 24-hour customer-service window are the compliance surface. Prototype the flow in OpenClaw; ship the customer path on the official API.
  • Low bandwidth. Default textChunkLimit is 4,000 characters and media caps at 50 MB — both are generous for a 2G/3G handset. Lower the chunk limit, prompt for short replies, and avoid image_generate/tts on the customer path unless the user asked for it. Every reply is billable data on the recipient's bundle.
  • Swahili needs no OpenClaw configuration — it is entirely a model-level concern. Put the language instruction in the agent's identity/system prompt and pick the model on Swahili quality (see the R&D report in research-and-development/).
  • Single-operator by design. OpenClaw is a personal assistant gateway: one host, one owner, multiple agents. It is not a multi-tenant backend, and trying to make one Gateway serve many customers' numbers fights the architecture. One Gateway per operator, or use it as an ops tool alongside a purpose-built Next.js app.

Troubleshooting

IssueFix
npm install -g openclaw skips setup / CLI missingnpm 12+ blocks lifecycle scripts — reinstall with --allow-scripts openclaw (pnpm: --allow-build=openclaw, bun: --trust)
Refuses to start: "refusing to bind gateway … without auth"Non-loopback bind requires gateway.auth.token/.password or OPENCLAW_GATEWAY_TOKEN
Control UI blocked in a remote browserAdd the origin to gateway.controlUi.allowedOrigins
Tunnel works but requests are rejectedSSH tunnels do not bypass gateway auth — the client must still send the token
WhatsApp QR expires on a headless boxGet the QR to your phone fast, or run openclaw channels login from a machine with a display and copy the credentials dir
Bot ignores an incoming DMdmPolicy: "pairing" is waiting on approval — openclaw pairing approve whatsapp <CODE> (expires after 1h, max 3 pending)
Messages arrive but no agent repliesWith agents.ownership: "explicit" there is no default agent — add a bindings entry for that channel/account
Skill installed but never usedCheck the name in SKILL.md frontmatter (it overrides the directory), the source precedence table, and whether disable-model-invocation: true is set
MCP tools missing from the agentopenclaw mcp doctor <name> --probe, then check toolFilter.include/exclude and tools.allow/deny
Duplicate outbound messages after a reconnectSend send/agent requests with idempotency keys
Node version errors on installNeeds Node 22.22.3+, 24.15+, or 25.9+ (26 recommended)