Multi-agent
Multi-agent routing
Run multiple isolated agents in one Gateway process, each with its own workspace, state directory (agentDir), and SQLite-backed session history, plus multiple channel accounts (e.g. two WhatsApp numbers). Inbound messages route to the right agent through bindings.
An agent is the full per-persona scope: workspace files, auth profiles, model registry, and session store. A binding maps a channel account (a Slack workspace, a WhatsApp number, etc.) to one of those agents.
For a focused setup guide with account and conversation examples, see Agent bindings.
What is one agent
Each agent has its own:
- Workspace: files,
AGENTS.md/SOUL.md/USER.md, local notes, persona rules. - State directory (
agentDir): auth profiles, model registry, per-agent config. - Session store: chat history and routing state in
<agentDir>/openclaw-agent.sqlite.
Auth profiles are per-agent, read from <agentDir>/openclaw-agent.sqlite. With the default layout, that resolves to:
~/.openclaw/agents/<agentId>/agent/openclaw-agent.sqliteSkills load from each agent workspace plus shared roots such as ~/.openclaw/skills, then filter by the effective agent skill allowlist. Use agents.defaults.skills for a shared baseline and agents.entries.*.skills for a per-agent replacement (explicit entries replace the default, they do not merge). See Skills: per-agent vs shared and Skills: agent allowlists.
Plugin-owned storage follows that plugin's configuration; adding a second agent does not automatically split every global plugin store. For example, configure Memory Wiki per-agent vaults when personas must not share compiled wiki knowledge.
Paths
| What | Default | Override |
|---|---|---|
| Config | ~/.openclaw/openclaw.json |
OPENCLAW_CONFIG_PATH |
| State dir | ~/.openclaw |
OPENCLAW_STATE_DIR |
| Default agent's workspace | <stateDir>/workspace (~/.openclaw-<profile>/workspace for a named profile) |
agents.entries.*.workspace, then agents.defaults.workspace, or OPENCLAW_WORKSPACE_DIR |
| Other agents' workspace | <stateDir>/workspace-<agentId> (or <agents.defaults.workspace>/<agentId> when set) |
agents.entries.*.workspace |
| Agent dir | ~/.openclaw/agents/<agentId>/agent |
agents.entries.*.agentDir |
| Sessions and transcripts | <agentDir>/openclaw-agent.sqlite |
agents.entries.*.agentDir |
| Legacy/archive session artifacts | ~/.openclaw/agents/<agentId>/sessions |
— |
Single-agent mode (default)
If you configure nothing, OpenClaw runs one agent:
agentIddefaults tomain.- The main session key is
agent:main:main. - Workspace defaults to
<stateDir>/workspace(~/.openclaw/workspacefor the default install and~/.openclaw-<profile>/workspacefor a named profile). - State defaults to
~/.openclaw/agents/main/agent.
Agent helper
Add a new isolated agent:
openclaw agents add workFlags: --workspace <dir>, --model <id>, --agent-dir <dir>, --bind <channel[:accountId]> (repeatable), --non-interactive (requires --workspace).
Add bindings to route inbound messages (the wizard offers to do this for you), then verify:
openclaw agents list --bindingsIn the Control UI, Settings → Agents updates model choices when the Gateway publishes a new catalog. Refreshing choices preserves your selected model, fallbacks, and identity draft. If the read fails, the editor shows an error and keeps the previous choices until a later update succeeds. Model and fallback edits keep their normal automatic save behavior.
Agent provenance
OpenClaw records how each configured agent was created: operator for CLI,
onboarding, and Gateway requests; agent when the system agent requested it;
and claw when a Claw install added it. Agent-created entries also retain the
requesting agent id. A configured agent can ask OpenClaw to create another
agent through its openclaw tool. The system agent files the typed operation,
shows the requesting agent id to the operator, and creates the agent only after
operator approval. Inspect the current creation hierarchy with:
openclaw agents list --treeDeleted creators remain historical provenance. If the creator is no longer in the configured roster, its children appear at the root of the tree.
Quick start
Create each agent workspace
openclaw agents add codingopenclaw agents add socialEach agent gets its own workspace with SOUL.md, AGENTS.md, and optional USER.md, plus a dedicated agentDir and session store. By default, those agent files live under ~/.openclaw/agents/<agentId>.
Create channel accounts
Create one account per agent on your preferred channels:
- Discord: one bot per agent, enable Message Content Intent, copy each token.
- Telegram: one bot per agent via BotFather, copy each token.
- WhatsApp: link each phone number per account.
openclaw channels login --channel whatsapp --account workAdd agents, accounts, and bindings
Add agents under agents.entries, channel accounts under channels.<channel>.accounts, and connect them with bindings (examples below).
Restart and verify
openclaw gateway restartopenclaw agents list --bindingsopenclaw channels status --probeMultiple agents, multiple personas
Each configured agentId is a distinct persona boundary for core agent state:
- Different accounts per channel (per
accountId). - Different personalities (per-agent
AGENTS.md/SOUL.md). - Separate auth and sessions, with cross-agent session access on by default and governed by
tools.agentToAgent. Narrow session visibility withtools.sessions.visibility, restrict agent pairs withtools.agentToAgent.allow, or settools.agentToAgent.enabled: falseto block ordinary cross-agent access. Requester-owned native subagent and ACP child sessions stay reachable undertreeorallvisibility; use separate gateways for strict separation.
This lets multiple people share one Gateway while keeping core agent state separate.
Per-agent Memory Wiki vaults
Memory Wiki uses one global vault by default. To keep a support agent's
compiled knowledge separate from a marketing agent's, set
plugins.entries.memory-wiki.config.vault.scope to agent:
{ plugins: { entries: { "memory-wiki": { enabled: true, config: { vault: { scope: "agent", path: "~/.openclaw/wiki", }, }, }, }, },}The configured path is the parent directory. OpenClaw appends the normalized
agent id, producing paths such as ~/.openclaw/wiki/support and
~/.openclaw/wiki/marketing. Agent-scoped CLI and Gateway operations require
an explicit agent when multiple agents are configured. See
Memory Wiki per-agent vaults for bridge
filtering, migration, and trust-boundary details.
Cross-agent memory search
The QMD cross-agent search path was removed in v2026.8.1 along with the rest
of the QMD backend. Builtin memory does not search
another agent's transcript corpus; each agent searches only its own configured
memory and eligible same-agent session sources. Put intentionally shared
Markdown in an explicit shared memory.search.extraPaths directory when the
same reference material should be indexed by multiple agents. For the full
upgrade path, see Migrating from QMD.
One WhatsApp number, multiple people (DM split)
Route different WhatsApp DMs to different agents on one WhatsApp account by matching sender E.164 (+15551234567) with peer.kind: "direct". Replies still come from the same WhatsApp number — there is no per-agent sender identity.
{ agents: { entries: { alex: { default: true, workspace: "~/.openclaw/workspace-alex" }, mia: { workspace: "~/.openclaw/workspace-mia" }, }, }, bindings: [ { agentId: "alex", match: { channel: "whatsapp", peer: { kind: "direct", id: "+15551230001" } }, }, { agentId: "mia", match: { channel: "whatsapp", peer: { kind: "direct", id: "+15551230002" } }, }, ], channels: { whatsapp: { dmPolicy: "allowlist", allowFrom: ["+15551230001", "+15551230002"], }, },}DM access control (pairing/allowlist) is global per WhatsApp account, not per agent. For shared groups, bind the group to one agent or use Broadcast groups.
Routing rules
Bindings are deterministic and most-specific wins. See Channel routing for the full tier order (exact peer, parent peer, peer wildcard, guild+roles, guild, team, account, channel, default agent). A few rules worth calling out here:
- If multiple bindings match within the same tier, the first one in config order wins.
- If a binding sets multiple match fields (for example
peer+guildId), all specified fields must match (ANDsemantics). - A binding that omits
accountIdmatches only the default account, not every account. UseaccountId: "*"for a channel-wide fallback, oraccountId: "<name>"for one account. Adding the same binding again with an explicit account id upgrades the existing channel-only binding instead of duplicating it.
For existing multi-agent configs, openclaw doctor --fix materializes legacy ambient default routing into channel-wide bindings plus explicit heartbeat, Custodian, and Talk targets. Single-agent configs are unchanged.
For a multi-agent roster defined directly in the main config file without a
legacy default: true marker, Doctor adds agents.ownership: "explicit" for
both keyed agents.entries and older agents.list rosters, including with
--fix --non-interactive. Existing bindings and per-surface owners remain
unchanged. Last-known-good recovery applies the same ownership stamp before
validating and restoring a directly authored markerless roster.
If an account has no fallback route but its matchable narrower bindings
all explicitly name one configured agent, Doctor adds an account-scoped binding for that
agent. It does not borrow ownership from another account or channel, choose
between conflicting owners, or assign other unowned surfaces.
When migrating a legacy agents.list roster without a default marker, Doctor
also pins the first agent's inherited workspace to agents.entries.<id>.workspace. Its customized instructions
and historical memory/ notes remain in their original directory. Explicit
workspaces stay authoritative. If an earlier upgrade already left two edited
workspaces, select the intended per-agent workspace and reconcile their contents
from your backups; Doctor does not merge directories.
Multiple accounts / phone numbers
Channels that support multiple accounts (e.g. WhatsApp) use accountId to identify each login. Each accountId routes to its own agent, so one server can host multiple phone numbers without mixing sessions.
Set channels.<channel>.defaultAccount to choose the account used when accountId is omitted. When unset, OpenClaw falls back to default if present, otherwise the first configured account id (sorted).
Channels supporting multiple accounts: discord, feishu, googlechat, imessage, irc, line, mattermost, matrix, nextcloud-talk, nostr, signal, slack, telegram, whatsapp, zalo, zalouser.
Concepts
agentId: one "brain" (workspace, per-agent auth, per-agent session store).accountId: one channel account instance (e.g. WhatsApp accountpersonalvsbiz).binding: routes inbound messages to anagentIdby(channel, accountId, peer), and optionally guild/team ids.- Direct chats collapse to
agent:<agentId>:mainby default (the per-agent main session).
Platform examples
Discord bots per agent
Each Discord bot account maps to a unique accountId. Bind each account to an agent and keep allowlists per bot.
{ agents: { entries: { main: { default: true, workspace: "~/.openclaw/workspace-main" }, coding: { workspace: "~/.openclaw/workspace-coding" }, }, }, bindings: [ { agentId: "main", match: { channel: "discord", accountId: "default" } }, { agentId: "coding", match: { channel: "discord", accountId: "coding" } }, ], channels: { discord: { groupPolicy: "allowlist", accounts: { default: { token: "DISCORD_BOT_TOKEN_MAIN", guilds: { "123456789012345678": { channels: { "222222222222222222": { enabled: true, requireMention: false }, }, }, }, }, coding: { token: "DISCORD_BOT_TOKEN_CODING", guilds: { "123456789012345678": { channels: { "333333333333333333": { enabled: true, requireMention: false }, }, }, }, }, }, }, },}- Invite each bot to the guild and enable Message Content Intent.
- Tokens live in
channels.discord.accounts.<id>.token(default account can useDISCORD_BOT_TOKEN).
Telegram bots per agent
{ agents: { entries: { main: { default: true, workspace: "~/.openclaw/workspace-main" }, alerts: { workspace: "~/.openclaw/workspace-alerts" }, }, }, bindings: [ { agentId: "main", match: { channel: "telegram", accountId: "default" } }, { agentId: "alerts", match: { channel: "telegram", accountId: "alerts" } }, ], channels: { telegram: { accounts: { default: { botToken: "123456:ABC...", dmPolicy: "pairing", }, alerts: { botToken: "987654:XYZ...", dmPolicy: "allowlist", allowFrom: ["tg:123456789"], }, }, }, },}- Create one bot per agent with BotFather and copy each token.
- Tokens live in
channels.telegram.accounts.<id>.botToken(default account can useTELEGRAM_BOT_TOKEN). - For multiple bots in the same Telegram group, invite each bot and mention the one that should answer.
- Disable BotFather Privacy Mode for each group bot (
/setprivacy-> Disable), then remove and re-add the bot so Telegram applies the setting. - Allow groups with
channels.telegram.groups, or usegroupPolicy: "open"only for trusted group deployments. - Put sender user IDs in
groupAllowFrom. Group and supergroup IDs belong inchannels.telegram.groups, notgroupAllowFrom. - Bind by
accountIdso each bot routes to its own agent.
WhatsApp numbers per agent
Link each account before starting the gateway:
openclaw channels login --channel whatsapp --account personalopenclaw channels login --channel whatsapp --account biz~/.openclaw/openclaw.json (JSON5):
{ agents: { entries: { home: { default: true, name: "Home", workspace: "~/.openclaw/workspace-home", agentDir: "~/.openclaw/agents/home/agent", }, work: { name: "Work", workspace: "~/.openclaw/workspace-work", agentDir: "~/.openclaw/agents/work/agent", }, }, }, // Deterministic routing: first match wins (most-specific first). bindings: [ { agentId: "home", match: { channel: "whatsapp", accountId: "personal" } }, { agentId: "work", match: { channel: "whatsapp", accountId: "biz" } }, // Optional per-peer override (example: send a specific group to work agent). { agentId: "work", match: { channel: "whatsapp", accountId: "personal", peer: { kind: "group", id: "[email protected]" }, }, }, ], // On by default. Omitted/empty `allow` permits every agent pair; // list requester and target ids to restrict access, or set enabled: false to turn it off. tools: { agentToAgent: { allow: ["home", "work"], }, }, channels: { whatsapp: { accounts: { personal: { // Optional override. Default: ~/.openclaw/credentials/whatsapp/personal // authDir: "~/.openclaw/credentials/whatsapp/personal", }, biz: { // Optional override. Default: ~/.openclaw/credentials/whatsapp/biz // authDir: "~/.openclaw/credentials/whatsapp/biz", }, }, }, },}Common patterns
WhatsApp daily + Telegram deep work
Split by channel: route WhatsApp to a fast everyday agent and Telegram to an Opus agent.
{ agents: { entries: { chat: { default: true, name: "Everyday", workspace: "~/.openclaw/workspace-chat", model: "anthropic/claude-sonnet-4-6", }, opus: { name: "Deep Work", workspace: "~/.openclaw/workspace-opus", model: "anthropic/claude-opus-4-6", }, }, }, bindings: [ { agentId: "chat", match: { channel: "whatsapp", accountId: "*" } }, { agentId: "opus", match: { channel: "telegram", accountId: "*" } }, ],}These examples use accountId: "*" so the bindings keep working if you add accounts later. To route a single DM/group to Opus while keeping the rest on chat, add a match.peer binding for that peer — peer matches always win over channel-wide rules.
Same channel, one peer to Opus
Keep WhatsApp on the fast agent, but route one DM to Opus:
{ agents: { entries: { chat: { default: true, name: "Everyday", workspace: "~/.openclaw/workspace-chat", model: "anthropic/claude-sonnet-4-6", }, opus: { name: "Deep Work", workspace: "~/.openclaw/workspace-opus", model: "anthropic/claude-opus-4-6", }, }, }, bindings: [ { agentId: "opus", match: { channel: "whatsapp", accountId: "*", peer: { kind: "direct", id: "+15551234567" } }, }, { agentId: "chat", match: { channel: "whatsapp", accountId: "*" } }, ],}Peer bindings always win, so keep them above the channel-wide rule.
Family agent bound to a WhatsApp group
Bind a dedicated family agent to a single WhatsApp group, with mention gating and a tighter tool policy:
{ agents: { entries: { family: { default: true, name: "Family", workspace: "~/.openclaw/workspace-family", identity: { name: "Family Bot" }, groupChat: { mentionPatterns: ["@family", "@familybot", "@Family Bot"], }, sandbox: { mode: "all", scope: "agent", }, tools: { allow: [ "exec", "read", "sessions_list", "sessions_history", "sessions_send", "sessions_spawn", "session_status", ], deny: ["write", "edit", "apply_patch", "browser", "canvas", "nodes", "cron"], }, }, }, }, bindings: [ { agentId: "family", match: { channel: "whatsapp", peer: { kind: "group", id: "[email protected]" }, }, }, ],}Tool allow/deny lists are tools, not skills. If a skill needs to run a binary, ensure exec is allowed and the binary exists in the sandbox. For stricter gating, set agents.entries.*.groupChat.mentionPatterns and keep group allowlists enabled for the channel.
Per-agent sandbox and tool configuration
Each agent can have its own sandbox and tool restrictions:
{ agents: { entries: { personal: { default: true, workspace: "~/.openclaw/workspace-personal", sandbox: { mode: "off", // No sandbox for personal agent }, // No tool restrictions - all tools available }, family: { workspace: "~/.openclaw/workspace-family", sandbox: { mode: "all", // Always sandboxed scope: "agent", // One container per agent docker: { // Optional one-time setup after container creation setupCommand: "apt-get update && apt-get install -y git curl", }, }, tools: { allow: ["read"], // Only read tool deny: ["exec", "write", "edit", "apply_patch"], // Deny others }, }, }, },}This gives you:
- Security isolation: restrict tools for untrusted agents.
- Resource control: sandbox specific agents while keeping others on host.
- Flexible policies: different permissions per agent.
See Multi-agent sandbox and tools for detailed examples.
Related
- ACP agents — running external coding harnesses
- Channel routing — how messages route to agents
- Parallel specialist lanes — splitting one job across role-scoped agents
- Presence — agent presence and availability
- Session — session isolation and routing
- Sub-agents — spawning background agent runs
openclaw agents— create and inspect agents from the CLI